Android MVP:在Presenter中安全使用Context

在我的应用程序中,我使用ContentProvider并使用LoaderManager.LoaderCallbacks.

片段(查看)

 public class ArticleCatalogFragment extends BaseFragment implements ArticleCatalogPresenter.View, LoaderManager.LoaderCallbacks { @Override public Loader onCreateLoader(int id, Bundle args) { return onCreateArticleCatalogLoader(args); } @Override public void onLoadFinished(Loader 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 { View view; @Override public Loader onCreateLoader(int id, Bundle args) { return onCreateArticleCatalogLoader(args); } @Override public void onLoadFinished(Loader 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 loaderProvider; public ArticleCatalogPresenter(Provider 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 { 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所需的样板量,以防止内存泄漏。