Android MVP模式中的OOM
转载自: 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源码设计模式解析与实战
更多相关文章
- Android的碎片fragment和activity之前的关系 以及MVP模式详解
- android启动模式android:launchMode
- Android 飞行模式的设置(打开/关闭飞行模式,获取飞行状态状态)
- 获取Android当前网络类型(2g、3g、4g、wifi)以及手机型号、版本号
- Android String类型转换为float、double和int的工具类