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