打造Android的MVP模式

1.概述

代码是两层开发,业务逻辑被内嵌与用户界面中,我们为了更好的维护,有必要将行为分离出来。我认为MVP模式就是基于这样一个理念。每个人都有一个自己的MVP模式。

2.我所理解的MVP模式

  • Presenter:交互中间人,处理业务逻辑,一个领域类,当需要View的 时候通过接口通知View
  • View :用户界面,处理视图的显示,逻辑交给Presenter,一个展现类
  • Model:数据的存储

3.构建我的MVP模式

简要概述:展现类持有领域类的引用,职责就是与用户进行直接的信息交互,比如我们在Activity中持有Presenter引用,Activity的事件逻辑处理交给Presenter。领域类定义数据及其具体的逻辑处理,展现类的操作对象都指向领域类,领域类在需要展现类帮助的时候通过接口通知到展现类。
例如:
Activity中进行初始化的代码片段:

  data = new ArrayList<>();
        adapter = new MainAdapter(data);
        binding.mainRecyclerView.setHasFixedSize(true);
        binding.mainRecyclerView.setAdapter(adapter);
        binding.mainRecyclerView.setLayoutManager(new GridLayoutManager(self, 4));
//        binding.mainRecyclerView.addItemDecoration(new DividerGridItemDecoration(self));

        itemTouchHelper = new ItemTouchHelper(new SwipItemTouchCallback(adapter).setOnDragListener(this));
        itemTouchHelper.attachToRecyclerView(binding.mainRecyclerView);
        /**
         * 逻辑放在Presenter,比如获取数据
         */
        mPresenter.initData();

我们的Presenter中定义initData(),并进行具体逻辑,从各种途径获取到我们需要的数据,并且通过接口返回到的Activity:

 /**
     * 这里进行联网获取数据或者其他逻辑操作
     */
    public void initData() {
        ArrayList<String> list = new ArrayList<>();
        list.add("a");
        list.add("b");
        list.add("c");
        ...
          /**
         * 基类使用了弱引用,每次使用之前应该判空,防止莫名空指针
         */
        if (isViewAttached()) {
            /**
             * Presenter需要MainActivity帮助
             */
            getMvpView().setData(list);
        }

我们的Activity回调到setData(List list),获取数据并刷新界面

@Override
    public void setData(List<String> list) {
        data.addAll(list);
        adapter.notifyDataSetChanged();
    }

大致的调用就是这样一个流程,我们通过Presenter将界面和数据逻辑分离开来,这样我们不管数据是什么获取方式,我们都保证只需要改变initData的业务逻辑就可以了,保证的代码的维护性和整洁。

4.构建基类

  • 创建一个空接口,命名为MvpView
public interface MvpView {
}
  • 创建一个中间的人的接口,定义两个方法,关联和解除关联,主要用于和Activity的关联。有解除关联的方式是因为我们会持有Activity的引用对象,需要在Activity的生命周期中的消亡阶段解除。
public interface Presenter<V extends MvpView> {
    /**
     * 关联
     * @param mvpView
     */
    void attachView(V mvpView);

    /**
     * 解除关联
     */
    void detachView();
}
  • BasePresenter:用于构建基类的中间人,并持有一个MvpView的子类实现的弱引用
public class BasePresenter<V extends MvpView> implements Presenter<V> {
    //    private V mMvpView;
    protected Reference<V> mMvpView;

    @Override
    public void attachView(V mvpView) {
        this.mMvpView = new WeakReference<>(mvpView);
    }

    @Override
    public void detachView() {
        if (this.mMvpView != null) {
            this.mMvpView.clear();
            this.mMvpView = null;
        }
    }

    /**
     * 是否已经关联
     *
     * @return
     */
    public boolean isViewAttached() {
        return mMvpView != null && mMvpView.get() != null;
    }

    /**
     * 获取接口
     *
     * @return
     */
    public V getMvpView() {
        return mMvpView.get();
    }

    /**
     * 检查是否关联并抛出自定义异常
     */
    public void checkViewAttached() {
        if (!isViewAttached()) throw new MvpViewNotAttachedException();
    }


    public static class MvpViewNotAttachedException extends RuntimeException {
        public MvpViewNotAttachedException() {
            super("Please call Presenter.attachView(MvpView) before" +
                    " requesting data to the Presenter");
        }
    }
}

在这里我们使用弱引用,原因在于我们的中间人在实际开发中,我们经常是需要通过联网去获取数据,在我们还在联网获取的数据的时候。我们的视图层也就是我们的Activity可能就已经消亡了,但是我们还是会持有他的引用而造成内存泄露。

  • BaseActivity指定泛型,两个,分别是MvpView的子类实现和BasePrensenter的子类实现代码如下:
public abstract class BaseActivity<T extends BasePresenter> extends AppCompatActivity implements MvpView{
    protected T mPresenter;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getBinding();
        mPresenter = createPresenter();
        mPresenter.attachView(this);


        this.initViews(savedInstanceState);
        this.initData();
        this.initListeners();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mPresenter.detachView();
    }

    /**
     * create presenter
     * @return
     */
    protected abstract T createPresenter();

    /**
     * databinding setContentView()
     */
    protected abstract void getBinding();

    /**
     * initialize the view in the layout
     * @param savedInstanceState
     */
    protected void initViews(Bundle savedInstanceState) {

    }

    /**
     * initialize the Activity data
     */
    protected void initData() {

    }

    /**
     * initialize
     */
    protected void initListeners() {

    }
}

强制子类实现获取Presenter,并在Activity的生命周期的onCreate和onDestroy方法中进行关联与解关联。这样基类就构建完成了

5.使用MVP的正确姿势

直接上代码使用流程也都差不多,我习惯是从Activity开始的,有需要才去Prensenter中创建逻辑方法,然后在去定义接口回调到Activity.
Activity代码如下:

public class MainActivity extends BaseActivity<MainPresenter> implements SwipItemTouchCallback.OnDragListener,MainView {
    private MainActivity self = MainActivity.this;
    private ActivityMainBinding binding;
    private List<String> data;
    private MainAdapter adapter;
    private ItemTouchHelper itemTouchHelper;
    @Override
    protected MainPresenter createPresenter() {
        return new MainPresenter();
    }

    @Override
    protected void getBinding() {
        binding = DataBindingUtil.setContentView(self, R.layout.activity_main);
    }

    @Override
    protected void initViews(Bundle savedInstanceState) {
        super.initViews(savedInstanceState);
    }

    @Override
    protected void initData() {
        super.initData();
        data = new ArrayList<>();
        adapter = new MainAdapter(data);
        binding.mainRecyclerView.setHasFixedSize(true);
        binding.mainRecyclerView.setAdapter(adapter);
        binding.mainRecyclerView.setLayoutManager(new GridLayoutManager(self, 4));
//        binding.mainRecyclerView.addItemDecoration(new DividerGridItemDecoration(self));

        itemTouchHelper = new ItemTouchHelper(new SwipItemTouchCallback(adapter).setOnDragListener(this));
        itemTouchHelper.attachToRecyclerView(binding.mainRecyclerView);
        /**
         * 逻辑放在Presenter,比如获取数据
         */
        mPresenter.initData();
    }

    @Override
    protected void initListeners() {
        super.initListeners();
        binding.mainRecyclerView.addOnItemTouchListener(new OnRecyclerItemClickListener(binding.mainRecyclerView) {
            @Override
            public void onItemClick(RecyclerView.ViewHolder vh) {
                CUtils.showMsg(data.get(vh.getAdapterPosition()));
            }

            @Override
            public void onItemLongClick(RecyclerView.ViewHolder vh) {
                if (vh.getAdapterPosition()!=data.size()-1) {
                    itemTouchHelper.startDrag(vh);
                    CUtils.Vibrate(self, 70);
                }
            }
        });
    }

    @Override
    public void onFinishDrag() {
        CUtils.showMsg(getString(R.string.ok));
    }

    @Override
    public void setData(List<String> list) {
        data.addAll(list);
        adapter.notifyDataSetChanged();
    }
}

MainPresenter中间人的代码如下:

public class MainPresenter extends BasePresenter<MainView> {

    /**
     * 这里进行联网获取数据或者其他逻辑操作
     */
    public void initData() {
        ArrayList<String> list = new ArrayList<>();
        list.add("a");
        list.add("b");
        list.add("c");
        list.add("d");
        list.add("e");
        list.add("f");
        list.add("g");
        list.add("h");
        list.add("i");
        list.add("j");
        list.add("k");
        list.add("l");
        list.add("m");
        list.add("n");
        list.add("o");
        list.add("p");
        list.add("q");
        list.add("r");
        list.add("s");
        list.add("t");
        list.add("u");
        list.add("v");
        list.add("w");
        list.add("x");
        list.add("y");
        list.add("z");
        list.add("...");

        /**
         * 基类使用了弱引用,每次使用之前应该判空,防止莫名空指针
         */
        if (isViewAttached()) {
            /**
             * Presenter需要MainActivity帮助
             */
            getMvpView().setData(list);
        }

    }
}

这里值得一提的是因为使用的是弱引用,所以我们在每次去获取Activity实例的时候都需要进行一次判空操作。也许会影响性能吧。。。

MainView接口代码

“`
public interface MainView extends MvpView {

/**
 * 已经获取到数据,需要MainActivity显示数据
 * @param list
 */
void setData(List<String> list);

}

6.结语
后期可能会有修改,一切以github上的代码为最后标准
整个项目的sample地址:https://github.com/jiahuahuang/H-MVP
独自一个人摸索,有不对的欢迎指导

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值