MVP In Android

3,453 阅读4分钟

众所周知,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

按照MVP的设计模式,Model很清晰了就是我们抽象出来的数据模型,这个有的是只是Bean类型的数据模型,或者可以通过抽象接口来实现提供数据的Model模型,这个我认为都可以,看情况而定,有的时候过于拆分也会导致过度使用的问题出现。View类一般认为是Activity/Fragment这种和UI关联度高的控件。Presenter是抽象出来的代理类,处理逻辑问题。

如何将View和Presenter链接起来呢?我们使用了一个契约类的方式定义了View和Presenter的暴露接口。

Contract Interface

public class ExamArrangeContract {
         * @link ExamArrange 考场安排     */
    interface View extends BaseView {
        void initialRecycler(List 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 {
    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 adapter;
    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;
    }
    public void beginLoad(PtrFrameLayout frame) {
        examArrangePresenter.beginLoad(frame);
    }
    public void loadMore() {
        examArrangePresenter.loadMore();
    }
    public void onResume() {
        super.onResume();
        examArrangePresenter.start();
    }
    public void onDestroyView() {
        super.onDestroyView();
        ButterKnife.unbind(this);
    }
    public void setPresenter(ExamArrangeContract.Presenter presenter) {
        this.examArrangePresenter = presenter;
    }
    public void initialRecycler(List arranges) {
        adapter = new CommonAdapter(
                getContext(),
                R.layout.item_exam_room,
                arranges) {
            public void bind(ViewHolder holder, ExamArrange examArrange) {
            }
        };
        LinearLayoutManager manager = new LinearLayoutManager(getContext());
        baseRecycler.setLayoutManager(manager);
        baseRecycler.setAdapter(adapter);
        baseRecycler.addOnScrollListener(new EndlessRecyclerOnScrollListener(manager) {
            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);
    }
    public void notifyRefreshRecycler() {
        adapter.notifyDataSetChanged();
    }
    public void stopRefresh() {
        ptrFrameLayout.refreshComplete();
    }
    }

在setPresenter中绑定一个引用,然后在UI里面就可以调用数据了。

Presenter实现类

Presenter实现类

public class ExamArrangePresenter implements ExamArrangeContract.Presenter {
    private ExamArrangeContract.View mView;
    private ArrayList arranges;
    public ExamArrangePresenter(ExamArrangeContract.View mView) {
        this.mView = mView;
        this.mView.setPresenter(this);
        this.arranges = new ArrayList<>();
    }
    public void start() {
        mView.initialRecycler(arranges);
    }
    public void initialDataForRecycler() {
   ...
    }
    public void beginLoad(PtrFrameLayout frame) {
        for (int i = 0; i < 10; i++) {
            arranges.add(new ExamArrange());
        }
        mView.notifyRefreshRecycler();
        mView.stopRefresh();
    }
    public void loadMore() {
        simulateLoadMoreData();
    }
    private void simulateLoadMoreData() {
        Observable.timer(2, TimeUnit.SECONDS, AndroidSchedulers.mainThread())
                .map(new Func1() {
                    public Object call(Long aLong) {
                        loadMoreData();
                        mView.notifyRefreshRecycler();
                        return null;
                    }
                }).subscribe();
    }
    private void loadMoreData() {
        List 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 {
    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工程更为清晰。