簡體   English   中英

Android MVP:在Presenter中安全使用Context

[英]Android MVP: safe use Context in Presenter

在我的應用程序中,我使用ContentProvider並使用LoaderManager.LoaderCallbacks<Cursor>.

片段(查看)

public class ArticleCatalogFragment extends BaseFragment
        implements ArticleCatalogPresenter.View,
        LoaderManager.LoaderCallbacks<Cursor> {

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        return onCreateArticleCatalogLoader(args);
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {        
         data.registerContentObserver(new LoaderContentObserver(new Handler(), loader));
         updateUI(data);        
    }   

    private Loader onCreateArticleCatalogLoader(Bundle args) {
            int categoryId = args.getInt(CATEGORY_ID);
            Loader loader = new ArticleCatalogLoader(this.getActivity(), categoryId);            
            return loader;
    }

}

從MVP的角度來看,我需要:

主持人

public class ArticleCatalogPresenter extends BasePresenter
        implements LoaderManager.LoaderCallbacks<Cursor> {

    View view;

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        return onCreateArticleCatalogLoader(args);
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {        
         data.registerContentObserver(new LoaderContentObserver(new Handler(), loader));
         view.updateUI(data);        
    }               

    private Loader onCreateArticleCatalogLoader(Bundle args) {    
            int categoryId = args.getInt(CATEGORY_ID);
            Loader loader = new ArticleCatalogLoader(context, categoryId); // need Context
            return loader;
    }


    interface View {
        updateUI(Cursor data)
    }

}

所以,我需要Presenter中的上下文。

有一些細微差別:

  1. 主持人知道上下文 - 這很糟糕,Presenter不應該知道Android。

  2. 在Presenter中使用Context可能會導致內存泄漏。

我現在擔心如何避免內存泄漏等問題,以及如何在Presenter中最好地傳遞Context,使用Application Context或Activity / Fragment?

向Presenter添加上下文並不好,因為演示者負責業務邏輯。 要處理上下文,您需要讓Fragment / Activities在接口的幫助下使用Callbacks,這將說明在處理視圖時activity / fragment需要執行哪些操作。 片段/活動負責提供上下文。

例:

interface BaseContract {
        interface BaseView {
            //Methods for View
            void onDoSomething();
        }

        interface BasePresenter {
            void doSomething();

        }
    }

    class BaseMainPresenter implements BaseContract.BasePresenter {
        BaseContract.BaseView view;

        BaseMainPresenter(BaseContract.BaseView view) {
            this.view = view;
        }

        @Override
        public void doSomething() {
            if (view != null)
                view.onDoSomething();
        }
    }

    class DemoClass implements BaseContract.BaseView {

        //Create object of Presenter 

        /****
         * Example :
         * BaseMainPresenter baseMainPresenter = new BaseMainPresenter(this);
         */
        @Override
        public void onDoSomething() {
            //Deal with Context here.
        }
    }

只是不要將您的演示者注冊為Android特定的回調目標(例如BroadcastReceiverLoaderManager.LoaderCallbacks等)。 處理View(片段或活動)中的回調方法,並將所有相關數據傳遞給演示者。

如果您需要創建對象的Context ,請讓您的視圖創建此對象(因為它具有對Context的引用)。 在您的情況下,電話

Loader loader = new ArticleCatalogLoader(context, categoryId)

應該重構

view.createLoaderForCategory(categoryId)

像這樣的代碼

Loader loader = new ArticleCatalogLoader(context, categoryId);

導致不可測試的代碼。 您應該避免在代碼中創建“業務”對象,並讓其他人為您執行此操作(任何DI框架,如Dagger 2都是比自己處理它更好的選擇)

話雖如此,你的問題是DI很久以前解決的問題。 您是否需要任何對象的全新實例? 使用Provider

Provider是“提供”對象實例的對象。 所以沒有

Loader loader = new ArticleCatalogLoader(context, categoryId);

您將擁有

Loader loader = loaderProvider.get(categoryId);

所以你唯一需要的是這樣的事情:

public class ArticleCatalogPresenter ... {
    ...
    private final Provider<Loader> loaderProvider;

    public ArticleCatalogPresenter(Provider<Loader> loaderProvider, ...) {
        this.loaderProvider = loaderProvider;
        ...
    }

    private Loader onCreateArticleCatalogLoader(Bundle args) {    
        int categoryId = args.getInt(CATEGORY_ID);
        Loader loader = loaderProvider.get(categoryId); // no context needed anymore!
        return loader;
    }

}
 public class ArticleCatalogPresenter extends BasePresenter implements LoaderManager.LoaderCallbacks<Cursor> { View view; ... private Loader onCreateArticleCatalogLoader(Bundle args) { int categoryId = args.getInt(CATEGORY_ID); Loader loader = new ArticleCatalogLoader(context, categoryId); // need Context return loader; } } 

因此,您希望Presenter中context構建ArticleCatalogLoader的新實例。 對?

如果是這樣,請通過構造函數將實例傳遞給Presenter 因此,當您要構建Presenter對象時,在Activity或DI容器中,執行以下操作:

ArticleCatalogPresenter articleCatalogPresenter=new ArticleCatalogPresenter(articleCatalogView,new ArticleCatalogLoader(context,categoryId));

這樣,您的Presenter將不依賴於context並且將完全可測試。

關於您對內存泄漏的關注,您可以通過在View中監聽onStop()然后調用Presenter中的相應方法來取消任何網絡請求或context相關任務,從而輕松避免這種context

我編寫了一個MVP庫 ,它可以節省大量MVP所需的樣板,以防止內存泄漏。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM