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

rhxh6433 3年前
   <h3>框架模式、设计模式、架构的关系</h3>    <ul>     <li>框架模式通常是对(面向相同行为代码的重用)代码的重用,是大智慧,用来对软件设计进行分工</li>     <li>设计模式通常是对(面向相同结构代码的重用)设计的重用,是小技巧,对具体问题提出解决方案,以提高代码复用率,降低耦合度</li>     <li>架构则介于框架和设计之间</li>     <li>我们所说的MVC、MVP、MVVM是一种框架模式而非设计模式</li>     <li>Android图片加载库的封装实战 即是通过设计模式完成对图片加载框架Glide进行封装的案例</li>    </ul>    <h3>问题发现</h3>    <p>MVP有很多优点,例如:</p>    <ul>     <li>易于维护</li>     <li>易于测试</li>     <li>松耦合</li>     <li>复用性高</li>     <li>健壮稳定</li>     <li>易于扩展</li>    </ul>    <p>但是,由于Presenter经常性地需要执行一些耗时操作,例如请求网络数据,而Presenter持有了Activity或者Fragment的强引用,如果在请求结束之前Activity或者Fragment被销毁了,那么由于网络请求还没有返回,导致Presenter一直持有Activity或者Fragment的对象,使得Activity或者Fragment对象无法回收,此时就发生了内存泄露</p>    <h2>MVP模式内存泄露问题的解决</h2>    <p>答案就是,通过弱引用和Activity、Fragment的生命周期来解决这个问题,首先建立一个Presenter对象,我们命名为BasePresenter,它是一个泛型类,泛型类型为View角色要实现的接口类型,具体代码如下:</p>    <pre>  <code class="language-java">/**   * 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<V> {        protected Reference<V> mViewRef;//View 接口类型的弱引用        public void attachView(V view) {          mViewRef = new WeakReference<V>(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();  }</code></pre>    <p>除了start和update两个公用的示范方法,BasePresenter还有4个方法,分别与View建立关联、解除关联、判断是否与View建立了关联、获取View。View类型通过BasePresenter的泛型类型传递进来,Presenter对这个View持有弱引用。通常情况下这个View类型应该是实现了某个特定接口的Activity或者Fragment等类型。</p>    <p>创建一个MVPBaseActivity基类,通过这个基类的生命周期函数来控制它与Presenter的关系,相关代码如下:</p>    <pre>  <code class="language-java">/**   * 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();  }</code></pre>    <p>MVPBaseActivity含有两个泛型参数,第一个是View接口类型,第二个是Presenter的具体类型,通过泛型参数,使得一些通用的逻辑可以抽象到MVPBaseActivity类中。例如,在MVPBaseActivity的onCreate函数中,会通过createPresenter函数创建一个具体的Presenter,这个Presenter的类型就是BasePresenter类型。构建Presenter之后调用attachView函数与Activity建立关联,而在onDestroy函数中,则会与Activity解除关联,从而避免内存泄露。</p>    <p>疑问:如果在onDestroy中解除了对Activity的引用,那么就没有必要再用弱引用了</p>    <p>解惑:并不是在任何情况下Activity的onDestroy都会被调用(其它原因导致Activity对象还在被引用,就不会回调onDestroy方法),一旦这种情况发生,弱引用也能够保证不会造成内存泄露。而通过MVPBaseActivity的封装维护Presenter与View关联关系的代码,使得子类可以避免重复的代码。</p>    <p>当然我们也可以把同样的思想用到更广阔的范围,例如可以为Fragment或者FragmentActivity建立类似这样的基类</p>    <pre>  <code class="language-java">/**   * 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();  }</code></pre>    <p>补充:其实我们在Activity中嵌套Fragment的情况下也可以通过如下方式将Presenter从Activity注入到Fragment</p>    <pre>  <code class="language-java">public interface BaseView<P> {        //这个可以在Activity中包裹Fragment的时候应用,这时候继承MVPBaseActivity      //Activity中初始化Presenter的实例 ,然后通过view调用该方法将Presenter塞给Fragment      void setPresenter(P presenter);  }</code></pre>    <h2>定义自己的MVP框架</h2>    <p>提供一个MVPContract ,封装MVP需要的所有基础接口,View和InteractionListener中使用的泛型为加载的数据类型,假设为MVPItem类型</p>    <pre>  <code class="language-java">public class MVPContract {        public interface Model {          //请求数据          void loadContent(boolean isLoadMore, String lastKey);      }        public interface View<T> {          //销毁加载页面          void dismissLoadingViews();          //展示加载页面          void showLoadingViews();          //展示异常页面          void showErrorViews(int errorCode, String msg);          //刷新块数据的内容          void refreshContentView(ArrayList<T> contentList);          //加载更多块数据的内容          void loadMoreContentView(ArrayList<T> contentList);      }        public interface Presenter {          //下拉刷新请求          void requestRefresh();          //加载更多数据          void requestLoadMore();      }        public interface InteractionListener<T> {          //请求成功          void onInteractionSuccess(T t);          //请求失败          void onInteractionFail(int errorCode, String errorMsg);      }    }</code></pre>    <p>实现自己的Presenter,命名为MVPPresenter</p>    <pre>  <code class="language-java">public class MVPPresenter extends BasePresenter<MVPContract .View<MVPItem>> implements MVPContract .InteractionListener<ArrayList<MVPItem>>,MVPContract .Presenter{        private MVPContract .View<MVPItem> mView;      private MVPContract .Model mModel;      private String param1;      private ArrayList<MVPItem> mList;      private boolean isLoading = false;      private boolean isLoadMore = false;        public MVPPresenter (String param, MVPContract .View<MVPItem> view){          this.param= param;          this.mView = view;          mModel = new MVPModel(param,this);      }        @Override      public void onInteractionSuccess(ArrayList<MVPItem> 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() {        }  }</code></pre>    <p>实现自己的Model,命名为MVPModel</p>    <pre>  <code class="language-java">public class MVPModel implements MVPContract.Model {        private MVPContract.InteractionListener<ArrayList<MVPItem>> mListener;        private String param;        public MVPModel(String param, MVPContract.InteractionListener<ArrayList<MVPItem>> listener) {          this.param = param;          this.mListener = listener;      }        @Override      public void loadContent(boolean isLoadMore, String lastKey) {          //网络请求          //数据处理          //成功或者失败的回调          //伪代码          if(success){          mListener.onInteractionSuccess("结果数据");          }else{          mListener.onInteractionFail("错误码","错误信息");          }      }  }</code></pre>    <p>例如MVPFragment 继承自MVPBaseFragment的实现如下:</p>    <p>此时,Presenter的创建以及与View建立关联等操作都被封装到MVPBaseFragment中,消除了子类重复代码的同时又避免了内存泄露的问题</p>    <pre>  <code class="language-java">public class MVPFragment extends MVPBaseFragment<MVPContract.View<MVPItem>, MVPPresenter> implements MVPContract.View<MVPItem>, 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<MVPItem> contentList) {        }        @Override      public void loadMoreContentView(ArrayList<MVPItem> contentList) {        }        @Override      public void onRefresh() {          mPresenter.requestRefresh();      }        @Override      public void onLoadMore() {          mPresenter.requestLoadMore();      }      }</code></pre>    <p>这里的MVPListView.IListener如下:(仅做参考)</p>    <pre>  <code class="language-java">/**       * Implements this interface to get refresh/load more event.       */      public interface IListener{            public void onRefresh();            public void onLoadMore();      }</code></pre>    <h2>小结</h2>    <ul>     <li>从整体效果来说,MVP是开发过程中非常值得推荐的架构模式,它能够将各组件进行解耦,并且带来良好的可扩展性、可测试性、稳定性、可维护性,同时使得每个类型的职责相对单一、简单,避免了大量的“胖”的程序存在,例如数千行的Activity类。它有效的将业务逻辑、数据处理等工作从Activity等View元素中抽离出来,使得每个类尽可能简单,同时每个模块能够独立进行演化。它的思想也非常好地体现了面向对象的设计原则,即抽象、单一指责、最小化、低耦合。</li>     <li>当然,需要说明的是,你的项目并不一定非要用MVP/MVVM或者别的什么模式,模式都有它们自身应用的范围,利弊并存,但了解是必需的,不然你很难扩展自己的技能范围,高级的开发应该学会判断某个项目是否合适一些现成的模式,合理的使用模式会让你的应用框架更加清晰并且易于维护和扩展。</li>    </ul>    <h2>参考</h2>    <p>Android源码设计模式解析与实战</p>    <p> </p>    <p>来自:http://blog.csdn.net/s003603u/article/details/56670819</p>    <p> </p>