转载自: http://blog.csdn.net/s003603u/article/details/56670819


前言

回顾

之前对MVP做过相关研究《Android-architecture之MVC、MVP、MVVM、Data-Binding》,也做过简单的分享《Android Architecture(Is Activity God?)》,在《列表全家桶之刷新、加载更多、悬浮、左滑删除》中也提到过希望对MVP相关做进一步的升级,抽取出了BasePresenter、BaseView、MVPBaseActivity、MVPBaseFragment,并通过使用弱引用预防可能发生的内存泄露问题 ,遂抽空着手整理一下

框架模式、设计模式、架构的关系

  • 框架模式通常是对(面向相同行为代码的重用)代码的重用,是大智慧,用来对软件设计进行分工
  • 设计模式通常是对(面向相同结构代码的重用)设计的重用,是小技巧,对具体问题提出解决方案,以提高代码复用率,降低耦合度
  • 架构则介于框架和设计之间
  • 我们所说的MVC、MVP、MVVM是一种框架模式而非设计模式
  • Android图片加载库的封装实战即是通过设计模式完成对图片加载框架Glide进行封装的案例

问题发现

MVP有很多优点,例如:

  • 易于维护
  • 易于测试
  • 松耦合
  • 复用性高
  • 健壮稳定
  • 易于扩展

但是,由于Presenter经常性地需要执行一些耗时操作,例如请求网络数据,而Presenter持有了Activity或者Fragment的强引用,如果在请求结束之前Activity或者Fragment被销毁了,那么由于网络请求还没有返回,导致Presenter一直持有Activity或者Fragment的对象,使得Activity或者Fragment对象无法回收,此时就发生了内存泄露

MVP模式内存泄露问题的解决

答案就是,通过弱引用和Activity、Fragment的生命周期来解决这个问题,首先建立一个Presenter对象,我们命名为BasePresenter,它是一个泛型类,泛型类型为View角色要实现的接口类型,具体代码如下:

/** * Author:    SuS * Version    V1.0 * Date:      17/02/23  * Description:通过弱引用和Activity以及Fragment的生命周期预防内存泄露的问题 * Modification  History: * Date             Author              Version         Description * ----------------------------------------------------------------------------------- * 17/02/23      SuS            1.0                    1.0 * Why & What is modified: */public abstract class BasePresenter {    protected Reference mViewRef;//View 接口类型的弱引用    public void attachView(V view) {        mViewRef = new WeakReference(view);    }    protected V getView() {        return mViewRef.get();    }    public boolean isViewAttached() {        return mViewRef != null && mViewRef.get() != null;    }    public void detachView() {        if (mViewRef != null) {            mViewRef.clear();            mViewRef = null;        }    }    //每个Presenter都会有初始化的工作,可以在这里统一处理    // 当然也可以不处理,这里只是一个公用的示范方法    public abstract void start();    //这里也可以理解为一个公用的示范方法    public abstract void update();}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

除了start和update两个公用的示范方法,BasePresenter还有4个方法,分别与View建立关联、解除关联、判断是否与View建立了关联、获取View。View类型通过BasePresenter的泛型类型传递进来,Presenter对这个View持有弱引用。通常情况下这个View类型应该是实现了某个特定接口的Activity或者Fragment等类型。 
创建一个MVPBaseActivity基类,通过这个基类的生命周期函数来控制它与Presenter的关系,相关代码如下:

 /** * Author:    SuS * Version    V1.0 * Date:      17/02/23  * Description:MVP Activity基类 * Modification  History: * Date             Author              Version         Description * ----------------------------------------------------------------------------------- * 17/02/23      SuS            1.0                    1.0 * Why & What is modified: */public abstract class MVPBaseActivity<V,P extends BasePresenter<V>> extends BaseActivity {    private static final String TAG = "MVPBaseActivity";    protected P mPresenter;    @Override    public void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        mPresenter = createPresenter();//创建Presenter        mPresenter.attachView((V)this);    }    @Override    public void onDestroy() {        super.onDestroy();        mPresenter.detachView();    }    protected abstract P createPresenter();}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

MVPBaseActivity含有两个泛型参数,第一个是View接口类型,第二个是Presenter的具体类型,通过泛型参数,使得一些通用的逻辑可以抽象到MVPBaseActivity类中。例如,在MVPBaseActivity的onCreate函数中,会通过createPresenter函数创建一个具体的Presenter,这个Presenter的类型就是BasePresenter类型。构建Presenter之后调用attachView函数与Activity建立关联,而在onDestroy函数中,则会与Activity解除关联,从而避免内存泄露。 
疑问:如果在onDestroy中解除了对Activity的引用,那么就没有必要再用弱引用了 
解惑:并不是在任何情况下Activity的onDestroy都会被调用(其它原因导致Activity对象还在被引用,就不会回调onDestroy方法),一旦这种情况发生,弱引用也能够保证不会造成内存泄露。而通过MVPBaseActivity的封装维护Presenter与View关联关系的代码,使得子类可以避免重复的代码。 
当然我们也可以把同样的思想用到更广阔的范围,例如可以为Fragment或者FragmentActivity建立类似这样的基类

 /** * Author:    SuS * Version    V1.0 * Date:      17/02/23  * Description:MVP Fragment基类 * Modification  History: * Date             Author              Version         Description * ----------------------------------------------------------------------------------- * 17/02/23      SuS            1.0                    1.0 * Why & What is modified: */public abstract class MVPBaseFragment<V,P extends BasePresenter<V>> extends BaseFragment {    private static final String TAG = "MVPBaseFragment";    protected P mPresenter;    @Override    public void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        mPresenter = createPresenter();//创建Presenter        mPresenter.attachView((V)this);    }    @Override    public void onDestroy() {        super.onDestroy();        mPresenter.detachView();    }    protected abstract P createPresenter();}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

补充:其实我们在Activity中嵌套Fragment的情况下也可以通过如下方式将Presenter从Activity注入到Fragment

public interface BaseView

{ //这个可以在Activity中包裹Fragment的时候应用,这时候继承MVPBaseActivity //Activity中初始化Presenter的实例 ,然后通过view调用该方法将Presenter塞给Fragment void setPresenter(P presenter);}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

定义自己的MVP框架

提供一个MVPContract ,封装MVP需要的所有基础接口,View和InteractionListener中使用的泛型为加载的数据类型,假设为MVPItem类型

public class MVPContract {    public interface Model {        //请求数据        void loadContent(boolean isLoadMore, String lastKey);    }    public interface View {        //销毁加载页面        void dismissLoadingViews();        //展示加载页面        void showLoadingViews();        //展示异常页面        void showErrorViews(int errorCode, String msg);        //刷新块数据的内容        void refreshContentView(ArrayList contentList);        //加载更多块数据的内容        void loadMoreContentView(ArrayList contentList);    }    public interface Presenter {        //下拉刷新请求        void requestRefresh();        //加载更多数据        void requestLoadMore();    }    public interface InteractionListener {        //请求成功        void onInteractionSuccess(T t);        //请求失败        void onInteractionFail(int errorCode, String errorMsg);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

实现自己的Presenter,命名为MVPPresenter

public class MVPPresenter extends BasePresenter<MVPContract .View<MVPItem>> implements MVPContract .InteractionListener<ArrayList<MVPItem>>,MVPContract .Presenter{    private MVPContract .View mView;    private MVPContract .Model mModel;    private String param1;    private ArrayList mList;    private boolean isLoading = false;    private boolean isLoadMore = false;    public MVPPresenter (String param, MVPContract .View view){        this.param= param;        this.mView = view;        mModel = new MVPModel(param,this);    }    @Override    public void onInteractionSuccess(ArrayList list) {        isLoading = false;        if(isLoadMore){            this.mList.addAll(list);            mView.loadMoreContentView(list);        } else {            this.mList = list;            mView.refreshContentView(list);        }        mView.dismissLoadingViews();    }    @Override    public void onInteractionFail(int errorCode, String errorMsg) {        isLoading = false;        mView.dismissLoadingViews();        mView.showErrorViews(errorCode, errorMsg);    }    @Override    public synchronized void requestRefresh() {        if (isLoading) {            return;        }        isLoading = true;        isLoadMore = false;        mModel.loadContent(false,null);    }    @Override    public synchronized void requestLoadMore() {        if (isLoading) {            return;        }        if (mList == null || mList.size() == 0) {            return;        }        isLoading = true;        isLoadMore = true;        mModel.loadContent(true,mList.get(mList.size() - 1).getKey());    }    @Override    public void start() {        if (isLoading) {            return;        }        isLoading = true;        isLoadMore = false;        mView.showLoadingViews();        mModel.loadContent(false,null);    }    @Override    public void update() {    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75

实现自己的Model,命名为MVPModel

public class MVPModel implements MVPContract.Model {    private MVPContract.InteractionListener> mListener;    private String param;    public MVPModel(String param, MVPContract.InteractionListener> listener) {        this.param = param;        this.mListener = listener;    }    @Override    public void loadContent(boolean isLoadMore, String lastKey) {        //网络请求        //数据处理        //成功或者失败的回调        //伪代码        if(success){        mListener.onInteractionSuccess("结果数据");        }else{        mListener.onInteractionFail("错误码","错误信息");        }    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

例如MVPFragment 继承自MVPBaseFragment的实现如下: 
此时,Presenter的创建以及与View建立关联等操作都被封装到MVPBaseFragment中,消除了子类重复代码的同时又避免了内存泄露的问题

public class MVPFragment extends MVPBaseFragment, MVPPresenter> implements MVPContract.View, MVPListView.IListener{    private static final String TAG = MVPFragment.class.getSimpleName();    private String param;    public MVPFragment() {        // Required empty public constructor    }    public static MVPFragment newInstance(String param) {        MVPFragment fragment = new MVPFragment();        Bundle args = new Bundle();        args.putString("param", param);        fragment.setArguments(args);        return fragment;    }    @Override    public void onCreate(Bundle savedInstanceState) {        if (getArguments() != null) {            param = getArguments().getString("param");        }        super.onCreate(savedInstanceState);    }    @Override    protected MVPPresenter createPresenter() {        return new MVPPresenter(param, this);    }    @Override    public View onCreateView(LayoutInflater inflater, ViewGroup container,                             Bundle savedInstanceState) {        // Inflate the layout for this fragment        View v = inflater.inflate(R.layout.fragment_mvp, container, false);        initView(v);        return v;    }    @Override    public void onActivityCreated(@Nullable Bundle savedInstanceState) {        super.onActivityCreated(savedInstanceState);        initData();    }    private void initView(View v) {    }    private void initData() {        mPresenter.start();    }    @Override    public void dismissLoadingViews() {    }    @Override    public void showLoadingViews() {    }    @Override    public void showErrorViews(int errorCode, String msg) {    }    @Override    public void refreshContentView(ArrayList contentList) {    }    @Override    public void loadMoreContentView(ArrayList contentList) {    }    @Override    public void onRefresh() {        mPresenter.requestRefresh();    }    @Override    public void onLoadMore() {        mPresenter.requestLoadMore();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92

这里的MVPListView.IListener如下:(仅做参考)

    /**     * Implements this interface to get refresh/load more event.     */    public interface IListener{        public void onRefresh();        public void onLoadMore();    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

小结

  • 从整体效果来说,MVP是开发过程中非常值得推荐的架构模式,它能够将各组件进行解耦,并且带来良好的可扩展性、可测试性、稳定性、可维护性,同时使得每个类型的职责相对单一、简单,避免了大量的“胖”的程序存在,例如数千行的Activity类。它有效的将业务逻辑、数据处理等工作从Activity等View元素中抽离出来,使得每个类尽可能简单,同时每个模块能够独立进行演化。它的思想也非常好地体现了面向对象的设计原则,即抽象、单一指责、最小化、低耦合。
  • 当然,需要说明的是,你的项目并不一定非要用MVP/MVVM或者别的什么模式,模式都有它们自身应用的范围,利弊并存,但了解是必需的,不然你很难扩展自己的技能范围,高级的开发应该学会判断某个项目是否合适一些现成的模式,合理的使用模式会让你的应用框架更加清晰并且易于维护和扩展。

参考

Android源码设计模式解析与实战



更多相关文章

  1. Android的碎片fragment和activity之前的关系 以及MVP模式详解
  2. android启动模式android:launchMode
  3. Android 飞行模式的设置(打开/关闭飞行模式,获取飞行状态状态)
  4. 获取Android当前网络类型(2g、3g、4g、wifi)以及手机型号、版本号
  5. Android String类型转换为float、double和int的工具类

随机推荐

  1. 华为外包程序员跳楼,难道这是我的35岁?
  2. 74.格式化xml脚本
  3. 虚拟机VBox安装CentOS6.8,内外网访问
  4. 接口限流的四种算法
  5. 分布式事务一致性
  6. 阿里 P8 病逝,自如甲醛房是元凶?
  7. Kafka为什么速度快
  8. 别扯了,学Flutter,Android 就不会凉?
  9. 我又一次被 Google Developer Day 给丑拒
  10. 有经验的程序员千万别被技术绑架了!