基于 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
  • 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>

    除了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(); }</code></pre>

    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(); }</code></pre>

    補充:其實我們在Activity中嵌套Fragment的情況下也可以通過如下方式將Presenter從Activity注入到Fragment

    public interface BaseView<P> {

    //這個可以在Activity中包裹Fragment的時候應用,這時候繼承MVPBaseActivity //Activity中初始化Presenter的實例 ,然后通過view調用該方法將Presenter塞給Fragment void setPresenter(P presenter); }</code></pre>

    定義自己的MVP框架

    提供一個MVPContract ,封裝MVP需要的所有基礎接口,View和InteractionListener中使用的泛型為加載的數據類型,假設為MVPItem類型

    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>

實現自己的Presenter,命名為MVPPresenter

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>

實現自己的Model,命名為MVPModel

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>

例如MVPFragment 繼承自MVPBaseFragment的實現如下:

此時,Presenter的創建以及與View建立關聯等操作都被封裝到MVPBaseFragment中,消除了子類重復代碼的同時又避免了內存泄露的問題

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>

這里的MVPListView.IListener如下:(僅做參考)

/**

 * Implements this interface to get refresh/load more event.
 */
public interface IListener{

    public void onRefresh();

    public void onLoadMore();
}</code></pre> 

小結

  • 從整體效果來說,MVP是開發過程中非常值得推薦的架構模式,它能夠將各組件進行解耦,并且帶來良好的可擴展性、可測試性、穩定性、可維護性,同時使得每個類型的職責相對單一、簡單,避免了大量的“胖”的程序存在,例如數千行的Activity類。它有效的將業務邏輯、數據處理等工作從Activity等View元素中抽離出來,使得每個類盡可能簡單,同時每個模塊能夠獨立進行演化。它的思想也非常好地體現了面向對象的設計原則,即抽象、單一指責、最小化、低耦合。
  • 當然,需要說明的是,你的項目并不一定非要用MVP/MVVM或者別的什么模式,模式都有它們自身應用的范圍,利弊并存,但了解是必需的,不然你很難擴展自己的技能范圍,高級的開發應該學會判斷某個項目是否合適一些現成的模式,合理的使用模式會讓你的應用框架更加清晰并且易于維護和擴展。

參考

Android源碼設計模式解析與實戰

 

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

 

 本文由用戶 rhxh6433 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!