美文网首页
MVP模式:从最简单的mvp开始

MVP模式:从最简单的mvp开始

作者: qh_七 | 来源:发表于2019-01-16 15:14 被阅读0次

    最近在进行项目的重构,也没怎么去看flutter,至今还处于入门阶段。
    就又东一榔头西一榔头的写一些关于mvp的东西了。不过总会继续写完的(只要flutter不凉,其实我甚至考虑要边看rxjava源码边开一篇文章记录学习(hhh
    闲话少说进入正题。

    1:关于mvp模式

    目前市面上使用的模式主流的有:MVC,MVP,MVVM三种。
    mcv模式是模型,视图,控制器三种不同功能的组合,你中有我我中有你。也不失为一种好的模型,毕竟剩下两种其实本质上来说也是基于mvc演变而来的。硬要说剩下两种就比mvc好也不见得。

    mvp呢是基于mvc的,实际上我认为这是一种降低耦合度版本的mvc: image.png
    图片来源于谷歌的mvp项目示例。单看右侧来说,这就是控制器与视图的交互,但是左边的model和视图的交互却由presenter来代理了,这就降低了耦合度。但是有一个不好的地方就是:会多出不少的代码需要你进行撰写。
    MVVM呢,目前是前端方面用的比较多,其实安卓也是有使用的,经典的使用就是MVVM+Databinding框架。谷歌现在开放的库有:
    image.png

    lifecycles 处理UI界面的生命周期,在26版本以后的Support库中,AppCompatActivity和SupportActivity中都实现了LifecycleOwner,内部已经对UI界面的生命周期做了处理了。
    而LiveData是一个抽象类,我们可以存放UI页面需要的数据,就是把数据包装在LiveData中了,我们可以观测LiveData中的数据变化,但是LiveData是跟UI的生命周期关联的,当UI页面销毁了,LiveData的数据变化回调是不会执行的。
    Room 就是一个sqlite数据持久化库,我们也可以使用别的ORM库。
    MVVM的响应式编程还是不错的,但是本文并不使用这个。说起来RN和flutter其实也是响应式编程。(大胆猜测,这可能是未来的主流

    2.关于mvp的使用

    先不看其他的示例,我们第一个实现的想法很自然:

    public class MainActivity extends  AppCompatActivity{
        ...
        private Presenter mPresenter;
        public void setPresenter(Presenter presenter){
          mPresenter = presenter;
        }
        ...
    }
    
    public class Presenter{
        private Model mModel;
        private MainActivity mView;
        public Presenter(Model model,aActivity view){
            mModel = model;
            mView = view;
            mView.setPresenter(this);
        }
    }
    

    这样就行了吗?开始你会觉得很自然,但是琢磨琢磨,是不是发现这三者的耦合度太高了,相比于mvc实际上并没有降低多少。那么怎么改进呢?持有实例耦合度太高,那咱们使用接口吧。
    接下来怎么做呢?咱们让Activity实现一个IView的接口,让Presenter实现IPresenter的接口。

    public class MainActivity extends AppCompatActivity implements IView{
      ....
        private IPresenter mPresenter;
        public void setPresenter(IPresenter presenter){
            mPresenter = presenter;
        }
    ....
    }
    
    public class Presenter implements IPresenter{
        private IModel mModel;
        private IView mView;
        public Presenter(IModel model,IView view){
            mModel = model;
            mView = view;
            mView.setPresenter(this);
        }
    }
    

    再把接口集中管理:

    public class MainActivityContract {
        public interface IPresenter{
        }
    
        public interface IModel {
        }
        public interface IView{
    
        }
    }
    

    其实这里以及和谷歌的示例差不多了。但是我们这里做了model的接口注入,而谷歌官方是没有的(这个可选可不选,谷歌之所以不用是为了保证全局唯一的数据层单例,就不能通过接口强转,避免数据污染。)

    那么还有没有可以优化的点呢?还是有的:实现更基础的V,P接口。

    public interface BaseView<T> {
    
        void setPresenter(T presenter);
    
    }
    
    public interface BasePresenter {
    
        void start();
    
    }
    
    

    这是谷歌官方的例子,先上网站:谷歌示例
    这里和谷歌不一样的是,谷歌选用了fragment作为View,而我们使用了Activity。
    为什么选用fragment呢?原因有以下两点:

    • 我们把 Activity 作为一个全局控制类来创建对象,把 Fragment 作为 view,这样两者就能各司其职。
    • 因为 Fragment 比较灵活,能够方便的处理界面适配的问题。
      而我们选用activity也没有问题,只要是作为view而不处理其他。

    3.再优化

    上面的代码是不是就完美了呢?其实并不是的,Presenter是持有view和model两方的,这里看起来没啥问题。但是你有没有想过,model是进行耗时操作呢?
    比如说一个网络请求,正常的retrofit来说,一个call<>方法是作为model,然后再presenter中实现,再将数据更新给view。看起来似乎是没什么问题,对吧?
    但是View是必须在主线程中运行的,而网络操作呢?是耗时的,为了避免ANR,必须要在异步线程中运行,那么这时候View关闭会发生什么呢?
    内存泄漏。这里View被持有,是强引用。


    1.png

    实线为强引用,w是windowmanager。仅做个例子,这里presenter对view持有,会导致无法被回收,GC是并不会回收强引用的,即使这时候内存不够了。
    那么我们的解决方法就呼之欲出了,使用软引用或者弱引用去取代强引用,为了能让activity被回收,这里我们使用哪种都行。
    我个人觉得弱引用是更好的(网上大部分也是弱引用),毕竟最先被回收的就是弱引用,当强引用断掉时立马回收。而软引用仅仅只是有可能回收。
    show my code.

    public abstract class BasePresenter<V>{
        private Reference<V> mViewReference;
    
        /**
         * 建立关联(弱引用)
         * @param view 界面
         */
        public void attachView(V view){
            mViewReference=new WeakReference<V>(view);
        }
    
        /**
         * 获取view
         * @return 持有界面
         */
        protected V getView(){
            if (isViewAttached()){
                return mViewReference.get();
            }
            return null;
        }
    
        /**
         * 判断view是否添加
         */
        public boolean isViewAttached(){
            return mViewReference != null && mViewReference.get()!=null;
        }
    
        /**
         * 取消关联
         */
        public void detachView(){
            if(mViewReference != null){
                mViewReference.clear();
                mViewReference = null;
            }
        }
    
        /**
         * 初始化
         */
        public abstract void start();
    }
    

    这样就完了吗?并不,我们还需要在View的生命周期中进行调用

     @Override
        public void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            mPresenter = createPresenter();
            if (mPresenter != null) {
                mPresenter.attachView((V) this);
            }
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
            if (mPresenter != null) {
                mPresenter.detachView();
            }
        }
    

    最终,我们的结构是这样的:

    Presenter:BasePresenter(基础行为,弱引用和初始化)+IPresenter(接口)
    以及我们的xxxxPresenter为具体实现。
    View:BaseView(生命周期调用以及创建presenter)+IView(接口) 以及具体实现类
    Model:使用全局单例(防止数据污染)或者如以上两个,使用接口注入。

    4.另一种实现

    其实还有另一种写法,因为MVP里有大量的依赖以及注入行为,代码会显得臃肿复杂,那么这时候就可以使用框架了:Dagger2时安卓的注入框架。
    我们可以使用Dagger2实现mvp模式。
    但是不好意思我还没学Dagger2,哈哈哈哈。
    dbq,等我学完了我再来补充这一部分。

    相关文章

      网友评论

          本文标题:MVP模式:从最简单的mvp开始

          本文链接:https://www.haomeiwen.com/subject/pscmdqtx.html