众所周知,Android的设计架构一直为人所诟病,模块的分割不清,很容易造成新手的困惑和迷茫,并且写出来的代码非常混杂,Activity即包含UI的处理,还包括数据的具体处理,让一个Activity弄出好几千行的容量,而且代码非常不清晰,可读性比较差。
所以在Android的开发过程中,一直没有一个统一的开发模式,MVC、MVP、MVVM都有出现,不过之前Google在Github开源的一个开源库to-do-mvp ,Google提供了他们对MVP的一个范式,我们一次为基础谈谈Android 的MVP的应用。
What is MVP?
MVP 指的是“models-views-presenters”的缩写,通过把逻辑操作和UI操作分离的方式,来让逻辑的结构更为清晰。Activity是一个通用的“God Object”什么都能放进去,导致了Android开发通常使用的是“models-views”的模式,仅仅把数据层单独的分离了出来,导致了逻辑操作放在了Activity里面。
MVP中的Presenter代理类是对MVC模式中Controller的一种更新,通过代理类和UI对象的绑定来实现逻辑操作的分离,View和Presenter可以互见,Model完全由Presenter操作,就是这种模式的核心理念。
Structure
这里参照Google的官方推荐标准来讲解如何使用MVP的模式。
按照MVP的设计模式,Model很清晰了就是我们抽象出来的数据模型,这个有的是只是Bean类型的数据模型,或者可以通过抽象接口来实现提供数据的Model模型,这个我认为都可以,看情况而定,有的时候过于拆分也会导致过度使用的问题出现。View类一般认为是Activity/Fragment这种和UI关联度高的控件。Presenter是抽象出来的代理类,处理逻辑问题。
如何将View和Presenter链接起来呢?我们使用了一个契约类的方式定义了View和Presenter的暴露接口。
Contract Interface
public class ExamArrangeContract {
/**
* @link ExamArrange 考场安排
*/
interface View extends BaseView<Presenter> {
void initialRecycler(List<ExamArrange> arranges);
void notifyRefreshRecycler();
void stopRefresh();
}
interface Presenter extends BasePresenter {
void initialDataForRecycler();
void beginLoad(PtrFrameLayout frame);
void loadMore();
}
}
契约类大多是形如以上代码的形式,重点定义了View和Presenter的暴露接口,里面定义了二者的职能。比如说View负责刷新RecyclerView的视图,Presenter负责给RecyclerView加载更多提供数据。
Activity/Fragment继承其中的View类,另外再定一个一个对Presenter的实现类就可以了。
Base Interface
public interface BaseView<T> {
void setPresenter(T presenter);
}
Contract类里面View继承的BaseView只有一个方法,就是和数据绑定。
public interface BasePresenter {
void start();
}
另外Presenter的Base类中也只提供了一个start()
函数用作Presenter的启动函数。一般放在Activity的onStart()
或者是onResume()
就可以了。
Implemention
实现类只需要分别继承契约类里面的接口就可以了。
下面放上跟那个契约类相关的实现方法。
首先是在Fragment里实现View接口
public class ExamArrangeFragment extends RecyclerFragment implements ExamArrangeContract.View {
public static ExamArrangeFragment newInstance() {
Bundle args = new Bundle();
ExamArrangeFragment fragment = new ExamArrangeFragment();
fragment.setArguments(args);
return fragment;
}
private ExamArrangeContract.Presenter examArrangePresenter;
private CommonAdapter<ExamArrange> adapter;
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View wrapper = super.onCreateView(inflater, container, savedInstanceState);
ButterKnife.bind(this, wrapper);
titleBar.setTitle("考场安排");
return wrapper;
}
@Override
public void beginLoad(PtrFrameLayout frame) {
examArrangePresenter.beginLoad(frame);
}
@Override
public void loadMore() {
examArrangePresenter.loadMore();
}
@Override
public void onResume() {
super.onResume();
examArrangePresenter.start();
}
@Override
public void onDestroyView() {
super.onDestroyView();
ButterKnife.unbind(this);
}
@Override
public void setPresenter(ExamArrangeContract.Presenter presenter) {
this.examArrangePresenter = presenter;
}
@Override
public void initialRecycler(List<ExamArrange> arranges) {
adapter = new CommonAdapter<ExamArrange>(
getContext(),
R.layout.item_exam_room,
arranges) {
@Override
public void bind(ViewHolder holder, ExamArrange examArrange) {
// holder.setText(R.id.exam_item_score_name, examArrange.getName());
}
};
LinearLayoutManager manager = new LinearLayoutManager(getContext());
baseRecycler.setLayoutManager(manager);
baseRecycler.setAdapter(adapter);
baseRecycler.addOnScrollListener(new EndlessRecyclerOnScrollListener(manager) {
@Override
public void onLoadMore(int currentPage) {
examArrangePresenter.loadMore();
}
});
createFooterView();
}
private void createFooterView() {
View view = LayoutInflater.from(getContext())
.inflate(R.layout.cube_views_load_more_default_footer,
baseRecycler, false);
HeaderViewRecyclerAdapter headerViewRecyclerAdapter = new HeaderViewRecyclerAdapter(adapter);
baseRecycler.setAdapter(headerViewRecyclerAdapter);
headerViewRecyclerAdapter.addFooterView(view);
}
@Override
public void notifyRefreshRecycler() {
adapter.notifyDataSetChanged();
}
@Override
public void stopRefresh() {
ptrFrameLayout.refreshComplete();
}
}
在setPresenter中绑定一个引用,然后在UI里面就可以调用数据了。
Presenter实现类
Presenter实现类
public class ExamArrangePresenter implements ExamArrangeContract.Presenter {
private ExamArrangeContract.View mView;
private ArrayList<ExamArrange> arranges;
public ExamArrangePresenter(ExamArrangeContract.View mView) {
this.mView = mView;
this.mView.setPresenter(this);
this.arranges = new ArrayList<>();
}
@Override
public void start() {
mView.initialRecycler(arranges);
}
@Override
public void initialDataForRecycler() {
...
}
@Override
public void beginLoad(PtrFrameLayout frame) {
for (int i = 0; i < 10; i++) {
arranges.add(new ExamArrange());
}
mView.notifyRefreshRecycler();
mView.stopRefresh();
}
@Override
public void loadMore() {
simulateLoadMoreData();
}
private void simulateLoadMoreData() {
Observable.timer(2, TimeUnit.SECONDS, AndroidSchedulers.mainThread())
.map(new Func1<Long, Object>() {
@Override
public Object call(Long aLong) {
loadMoreData();
mView.notifyRefreshRecycler();
return null;
}
}).subscribe();
}
private void loadMoreData() {
List<ExamArrange> moreList = new ArrayList<>();
for (int i = 1; i < 13; i++) {
ExamArrange arrange = new ExamArrange();
arrange.setName("fuck " + 1);
moreList.add(arrange);
}
arranges.addAll(moreList);
}
}
Bind
最后在Activity中将两者绑定。
public class ExamArrangeActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_exam_arrange);
ExamArrangeFragment fragment = (ExamArrangeFragment)
getSupportFragmentManager().findFragmentById(R.id.exam_arrangement_contain);
if (fragment == null) {
fragment = ExamArrangeFragment.newInstance();
ActivityUtils.addFragmentToActivity(getSupportFragmentManager(),
fragment, R.id.exam_arrangement_contain);
}
ExamArrangePresenter presenter = new ExamArrangePresenter(fragment);
}
}
最后再说两句
上面介绍的方法也只是MVP在Android上的一种实现方式,其实还有把Acivity当成presenter的做法,多种方式不一而足。不过使用了MVP架构之后UI和逻辑都更为清晰这也是显而易见的,希望大家能有所收获,让Android工程更为清晰。