概述
現(xiàn)在有許多安卓app采用mvp模式進行開發(fā).
mvp是什么?
m層指的是數(shù)據(jù)層, 對數(shù)據(jù)進行各種處理.
v層一般是指activity或fragment, 它只關注ui界面.
p層及presenter, 它像一個主持人一樣, 作為中間人調用m層與v層, 將它們隔離開來, 從而達到解耦目的.
問題一
然而我們在用mvp模式開發(fā)的時候, 會遇到這樣一些問題:
一般來講, Activity會持有Presenter的引用, Presenter也會持有Activity的引用, 這就導致了一個問題, 當Activity退出銷毀后, 由于 P層仍持有Activity的引用, 導致Activity無法釋放, 最終會引起內存泄漏! 怎么辦呢? 網(wǎng)上也有許多解決方法, 把這個Activity用弱引用包裹一下 :
public abstract class BasePresenter<V extends IView> {
/**弱引用, 防止內存泄漏*/
private WeakReference<V> weakReference;
/**
* 關聯(lián)V層和P層
*/
public void attatchView(V v) {
weakReference = new WeakReference<>(v);
}
/**
* @return P層和V層是否關聯(lián).
*/
public boolean isViewAttached() {
return weakReference != null && weakReference.get() != null;
}
/**
* 斷開V層和P層
* 在Acitivity的onDestory()中調用
*/
public void detachView() {
if (isViewAttached()) {
weakReference.clear();
weakReference = null;
}
}
...
}
問題二
當P層的邏輯處理完后, 我們就要調用V層來處理UI了, 怎么拿到V層的引用呢? 很簡單, 定義一個方法:
public V getView() {
return isViewAttached() ? weakReference.get() : null;
}
但是這個方法有個很讓人不安的返回值, 它有可能返回null. 試想一下, 用戶打開了一個頁面(Activity), 這個頁面的P層去網(wǎng)絡請求, 也許網(wǎng)絡比較卡, 用戶沒等結果返回, 就退出了該頁面.此時網(wǎng)絡請求仍在繼續(xù).直到好不容易有結果返回的時候, P層調用getView()方法去更新ui, activity弱引用已經釋放掉了, getView()就會返回null, 就會發(fā)生喜聞樂見的空指針異常!
怎么辦? 辦法也很簡單, 在getView()方法調用的時候, 加一層判斷:
if(isViewAttached()){
getView().xxxx
}
但作為一個偷懶的程序員, 我還是覺得這樣判斷太麻煩了, 有沒有一個自動幫我對getView()返回值判空的方法呢? 最好在BasePresenter類中都處理好了, 調用起來沒有后顧之憂就爽多了.
答案當然是有! 可以采用動態(tài)代理模式, 在attatchView()的時候, 生成Activity的代理類, 在每個Activity方法被調用之前判空下 Activity, 如果Activity存在, 就讓這個代理類執(zhí)行更新ui的方法, 如果被銷毀了, 就啥都不做. 廢話不多說, show code:
public abstract class BasePresenter<V extends IView> {
/**弱引用, 防止內存泄漏*/
private WeakReference<V> weakReference;
private V mProxyView;
/**
* 關聯(lián)V層和P層
*/
public void attatchView(V v) {
weakReference = new WeakReference<>(v);
MvpViewHandler viewHandler = new MvpViewHandler(weakReference.get());
mProxyView = (V) Proxy.newProxyInstance(v.getClass().getClassLoader(), v.getClass().getInterfaces(), viewHandler);
}
/**
* @return P層和V層是否關聯(lián).
*/
public boolean isViewAttached() {
return weakReference != null && weakReference.get() != null;
}
/**
* 斷開V層和P層
*/
public void detachView() {
if (isViewAttached()) {
weakReference.clear();
weakReference = null;
}
}
public V getView() {
return mProxyView;
}
private class MvpViewHandler implements InvocationHandler {
private IView mvpView;
MvpViewHandler(IView mvpView) {
this.mvpView = mvpView;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//如果V層沒被銷毀, 執(zhí)行V層的方法.
if (isViewAttached()) {
return method.invoke(mvpView, args);
}
//P層不需要關注V層的返回值
return null;
}
}
}