1. 程式人生 > >Android開發:淺談MVP模式應用與記憶體洩漏

Android開發:淺談MVP模式應用與記憶體洩漏

最近博主開始在專案中實踐MVP模式,卻意外發現記憶體洩漏比較嚴重,但卻很少人談到這個問題,促使了本文的釋出,本文假設讀者已瞭解MVP架構。

MVP簡介

M-Modle,資料,邏輯操作層,資料獲取,資料持久化儲存。比如網路操作,資料庫操作
V-View,介面展示層,Android中的具體體現為Activity,Fragment
P-Presenter,中介者,連線Modle,View層,同時持有modle引用和view介面引用
這裡寫圖片描述
上圖摘自阮一峰大神部落格:MVC,MVP 和 MVVM 的圖示

注:有別於MVC,Activity,Fragment通常被用作Controller和View使用,加重了它的職責。在MVP中,Activity,Fragment僅用做View層展示

示例程式碼

Modle層操作

public class TestModle implements IModle{
    private CallbackListener callback;

    public TestModle(CallbackListener callback) {
        this.callback = callback;
    }
    public interface CallbackListener {
        void onGetData(String data);
    }
    public void getData
() { new Thread() { public void run() { callback.onGetData("返回的資料"); } }.start(); } }

View層

// 抽象的view層
public interface TestViewInterf extends IView {
    void onGetData(String data);
}

// 具體的View層
public class MainActivity extends Activity
implements TestViewInterf{
private TestPresenter mTestPresenter; @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); // view層將獲取資料的任務委派給中介者presenter,並傳入自身例項物件,實現TestViewInterf介面 mTestPresenter = new TestPresenter(this); mTestPresenter.getData(); } @Override public void onGetData(String data) { // View層只做資料展示 showToast(data); } private void showToast(String toast) { Toast.makeText(this, toast, Toast.LENGTH_LONG).show(); } }

Presenter中介者

public class TestPresenter implements IPresenter{
    IModle modle;
    IView view;
    public TestPresenter(IView view) {
        this.view = view;
    }

    public void getData() {
        // 獲取資料的操作實際在Modle層執行
        modle = new TestModle(new CallbackListener() {
            public void onGetData(String data) {
                if (view != null) {
                    view.onGetData(data);
                }
            }
        });
        modle.getData();
    }
}

根據OOP思想,Java程式碼最好面向介面,抽象程式設計,這樣才能給符合OCP原則。上述示例程式碼省略了更加抽象的介面IModle,IView,IPresenter,並且實際MVP實踐中通常會引入泛型使其更具擴充套件性。

記憶體洩露問題

由上可見,Presenter中持有View介面物件,這個介面物件實際為MainActivity.this,Modle中也同時擁有Presenter物件例項,當MainActivity要銷燬時,Presenter中有Modle在獲取資料,那麼問題來了,這個Activity還能正常銷燬嗎?
答案是不能!
當Modle在獲取資料時,不做處理,它就一直持有Presenter物件,而Presenter物件又持有Activity物件,這條GC鏈不剪斷,Activity就無法被完整回收。
換句話說:Presenter不銷燬,Activity就無法正常被回收。

解決MVP的記憶體洩露

Presenter在Activity的onDestroy方法回撥時執行資源釋放操作,或者在Presenter引用View物件時使用更加容易回收的軟引用,弱應用。
比如示例程式碼:
Activity

@Override
    public void onDestroy() {
        super.onDestroy();
        mPresenter.destroy();
        mPresenter = null;
    }

Presenter

public void destroy() {
    view = null;
    if(modle != null) {
        modle.cancleTasks();
        modle = null;
    }
}

Modle

public void cancleTasks() {
    // TODO 終止執行緒池ThreadPool.shutDown(),AsyncTask.cancle(),或者呼叫框架的取消任務api
}

個人總結

因為面向MVP介面程式設計,可適應需求變更,所以MVP適用於比較大的專案;因為其簡化了Activity和Fragmnt的職責,可大大減少View層的程式碼量,比起MVC中Activity,Fragment動不動上千行的程式碼量,簡直優雅!

做完以上操作,由於MVP引起的記憶體洩露就差不多解決了,祝大家擼碼愉快!歡迎留言區交流指正。