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

最近在进行项目的重构,也没怎么去看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,等我学完了我再来补充这一部分。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容