Android開發:淺談MVP模式應用與內存泄漏

最近博主開始在項目中實踐MVP模式,卻意外發現內存泄漏比較嚴重,但卻很少人談到這個問題,促使了本文的發佈,本文假設讀者已瞭解MVP架構。

本文原創作者:xiong_it,鏈接:http://blog.csdn.net/xiong_it

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層展示

示例代碼

本文原創作者:xiong_it,鏈接:http://blog.csdn.net/xiong_it

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實踐中通常會引入泛型使其更具擴展性。

Google已提供了相關示例代碼,並在MVP中增加了一個約束者:Contract,它的作用是定義各個模塊的MVP接口。
google MVP sample code:https://github.com/googlesamples/android-architecture

內存泄露問題

由上可見,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
}

本文原創作者:xiong_it,鏈接:http://blog.csdn.net/xiong_it


個人總結

因爲面向MVP接口編程,可適應需求變更,所以MVP適用於比較大的項目;因爲其簡化了Activity和Fragmnt的職責,可大大減少View層的代碼量,比起MVC中Activity,Fragment動不動上千行的代碼量,簡直優雅!

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

發佈了69 篇原創文章 · 獲贊 251 · 訪問量 49萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章