基于 Activity、Fragment 的生命周期避免 MVP 模式内存泄露的问题

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

  • 框架模式通常是对(面向相同行为代码的重用)代码的重用,是大智慧,用来对软件设计进行分工

  • 设计模式通常是对(面向相同结构代码的重用)设计的重用,是小技巧,对具体问题提出解决方案,以提高代码复用率,降低耦合度

  • 架构则介于框架和设计之间

  • 我们所说的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 * Deion:通过弱引用和Activity以及Fragment的生命周期预防内存泄露的问题 * Modification History: * Date Author Version Deion * ———————————————————————————– * 17/02/23 SuS 1.0 1.0 * Why & What is modified: */publicabstractclassBasePresenter<V> { protectedReference<V> mViewRef; //View 接口类型的弱引用publicvoidattachView(V view){ mViewRef = newWeakReference<V>(view); } protectedV getView(){ returnmViewRef.get(); } publicbooleanisViewAttached(){ returnmViewRef != null&& mViewRef.get() != null; } publicvoiddetachView(){ if(mViewRef != null) { mViewRef.clear(); mViewRef = null; } } //每个Presenter都会有初始化的工作,可以在这里统一处理// 当然也可以不处理,这里只是一个公用的示范方法publicabstractvoidstart(); //这里也可以理解为一个公用的示范方法publicabstractvoidupdate();}

  除了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 * Deion:MVP Activity基类 * Modification History: * Date Author Version Deion * ———————————————————————————– * 17/02/23 SuS 1.0 1.0 * Why & What is modified: */publicabstractclassMVPBaseActivity<V,PextendsBasePresenter<V>> extendsBaseActivity{ privatestaticfinalString TAG = "MVPBaseActivity"; protectedP mPresenter; @OverridepublicvoidonCreate(@Nullable Bundle savedInstanceState){ super.onCreate(savedInstanceState); mPresenter = createPresenter(); //创建PresentermPresenter.attachView((V) this); } @OverridepublicvoidonDestroy(){ super.onDestroy(); mPresenter.detachView(); } protectedabstractP createPresenter();}

  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 * Deion:MVP Fragment基类 * Modification History: * Date Author Version Deion * ———————————————————————————– * 17/02/23 SuS 1.0 1.0 * Why & What is modified: */publicabstractclassMVPBaseFragment<V,PextendsBasePresenter<V>> extendsBaseFragment{ privatestaticfinalString TAG = "MVPBaseFragment"; protectedP mPresenter; @OverridepublicvoidonCreate(@Nullable Bundle savedInstanceState){ super.onCreate(savedInstanceState); mPresenter = createPresenter(); //创建PresentermPresenter.attachView((V) this); } @OverridepublicvoidonDestroy(){ super.onDestroy(); mPresenter.detachView(); } protectedabstractP createPresenter();}

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

  publicinterfaceBaseView<P> { //这个可以在Activity中包裹Fragment的时候应用,这时候继承MVPBaseActivity//Activity中初始化Presenter的实例 ,然后通过view调用该方法将Presenter塞给FragmentvoidsetPresenter(P presenter);} 定义自己的MVP框架

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

  publicclassMVPContract{ publicinterfaceModel{ //请求数据voidloadContent(booleanisLoadMore, String lastKey); } publicinterfaceView<T> { //销毁加载页面voiddismissLoadingViews(); //展示加载页面voidshowLoadingViews(); //展示异常页面voidshowErrorViews(interrorCode, String msg); //刷新块数据的内容voidrefreshContentView(ArrayList<T> contentList); //加载更多块数据的内容voidloadMoreContentView(ArrayList<T> contentList); } publicinterfacePresenter{ //下拉刷新请求voidrequestRefresh(); //加载更多数据voidrequestLoadMore(); } publicinterfaceInteractionListener<T> { //请求成功voidonInteractionSuccess(T t); //请求失败voidonInteractionFail(interrorCode, String errorMsg); }}

  实现自己的Presenter,命名为MVPPresenter

  publicclassMVPPresenterextendsBasePresenter<MVPContract.View<MVPItem>> implementsMVPContract.InteractionListener<ArrayList<MVPItem>>,MVPContract.Presenter{ privateMVPContract .View<MVPItem> mView; privateMVPContract .Model mModel; privateString param1; privateArrayList<MVPItem> mList; privatebooleanisLoading = false; privatebooleanisLoadMore = false; publicMVPPresenter(String param, MVPContract .View<MVPItem> view){ this.param= param; this.mView = view; mModel = newMVPModel(param, this); } @OverridepublicvoidonInteractionSuccess(ArrayList<MVPItem> list){ isLoading = false; if(isLoadMore){ this.mList.addAll(list); mView.loadMoreContentView(list); } else{ this.mList = list; mView.refreshContentView(list); } mView.dismissLoadingViews(); } @OverridepublicvoidonInteractionFail(interrorCode, String errorMsg){ isLoading = false; mView.dismissLoadingViews(); mView.showErrorViews(errorCode, errorMsg); } @OverridepublicsynchronizedvoidrequestRefresh(){ if(isLoading) { return; } isLoading = true; isLoadMore = false; mModel.loadContent( false, null); } @OverridepublicsynchronizedvoidrequestLoadMore(){ if(isLoading) { return; } if(mList == null|| mList.size() == 0) { return; } isLoading = true; isLoadMore = true; mModel.loadContent( true,mList.get(mList.size() – 1).getKey()); } @Overridepublicvoidstart(){ if(isLoading) { return; } isLoading = true; isLoadMore = false; mView.showLoadingViews(); mModel.loadContent( false, null); } @Overridepublicvoidupdate(){ }}

  实现自己的Model,命名为MVPModel

  publicclassMVPModelimplementsMVPContract.Model{ privateMVPContract.InteractionListener<ArrayList<MVPItem>> mListener; privateString param; publicMVPModel(String param, MVPContract.InteractionListener<ArrayList<MVPItem>> listener){ this.param = param; this.mListener = listener; } @OverridepublicvoidloadContent(booleanisLoadMore, String lastKey){ //网络请求//数据处理//成功或者失败的回调//伪代码if(success){ mListener.onInteractionSuccess( "结果数据"); } else{ mListener.onInteractionFail( "错误码", "错误信息"); } }}

  例如MVPFragment 继承自MVPBaseFragment的实现如下:

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

  publicclassMVPFragmentextendsMVPBaseFragment<MVPContract.View<MVPItem>, MVPPresenter> implementsMVPContract.View<MVPItem>, MVPListView.IListener{ privatestaticfinalString TAG = MVPFragment.class.getSimpleName(); privateString param; publicMVPFragment(){ // Required empty public constructor} publicstaticMVPFragment newInstance(String param){ MVPFragment fragment = newMVPFragment(); Bundle args = newBundle(); args.putString( "param", param); fragment.setArguments(args); returnfragment; } @OverridepublicvoidonCreate(Bundle savedInstanceState){ if(getArguments() != null) { param = getArguments().getString( "param"); } super.onCreate(savedInstanceState); } @OverrideprotectedMVPPresenter createPresenter(){ returnnewMVPPresenter(param, this); } @OverridepublicView onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){ // Inflate the layout for this fragmentView v = inflater.inflate(R.layout.fragment_mvp, container, false); initView(v); returnv; } @OverridepublicvoidonActivityCreated(@Nullable Bundle savedInstanceState){ super.onActivityCreated(savedInstanceState); initData(); } privatevoidinitView(View v){ } privatevoidinitData(){ mPresenter.start(); } @OverridepublicvoiddismissLoadingViews(){ } @OverridepublicvoidshowLoadingViews(){ } @OverridepublicvoidshowErrorViews(interrorCode, String msg){ } @OverridepublicvoidrefreshContentView(ArrayList<MVPItem> contentList){ } @OverridepublicvoidloadMoreContentView(ArrayList<MVPItem> contentList){ } @OverridepublicvoidonRefresh(){ mPresenter.requestRefresh(); } @OverridepublicvoidMore(){ mPresenter.requestLoadMore(); }}

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

  /** * Implements this interface to get refresh/load more event. */publicinterfaceIListener{ publicvoidonRefresh(); publicvoidMore(); } 小结

  • 从整体效果来说,MVP是开发过程中非常值得推荐的架构模式,它能够将各组件进行解耦,并且带来良好的可扩展性、可测试性、稳定性、可维护性,同时使得每个类型的职责相对单一、简单,避免了大量的“胖”的程序存在,例如数千行的Activity类。它有效的将业务逻辑、数据处理等工作从Activity等View元素中抽离出来,使得每个类尽可能简单,同时每个模块能够独立进行演化。它的思想也非常好地体现了面向对象的设计原则,即抽象、单一指责、最小化、低耦合。

  • 当然,需要说明的是,你的项目并不一定非要用MVP/MVVM或者别的什么模式,模式都有它们自身应用的范围,利弊并存,但了解是必需的,不然你很难扩展自己的技能范围,高级的开发应该学会判断某个项目是否合适一些现成的模式,合理的使用模式会让你的应用框架更加清晰并且易于维护和扩展。

参考

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

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

  群1已满

IT文库 » 基于 Activity、Fragment 的生命周期避免 MVP 模式内存泄露的问题
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址