Android MVP:在Presenter中安全使用Context

在我的应用程序中,我使用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的angular度来看,我需要:

主持人

 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. 主持人知道上下文 – 这是不好的,主持人不应该知道的Android。

  2. 在Presenter中有一个上下文可能会导致内存泄漏。

我现在担心如何避免内存泄漏等问题,以及如何最好地在Presenter中传递Context,使用Application Context或Activity / Fragment?

Solutions Collecting From Web of "Android MVP:在Presenter中安全使用Context"

向Presenter添加上下文并不好,因为演示者负责业务逻辑。 为了处理上下文,你需要通过接口的帮助让片段/活动使用callback,这些接口将说明在处理视图时,活动/片段需要执行什么操作。 片段/活动负责提供上下文。

例:

 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特定的callback目标(例如BroadcastReceiverLoaderManager.LoaderCallbacks等)。 处理视图(片段或活动)中的callback方法,并将所有相关数据传递给演示者。

如果你需要Context创build对象,让你的视图创build这个对象(因为它有一个对Context的引用)。 在你的情况下的电话

 Loader loader = new ArticleCatalogLoader(context, categoryId) 

应该被重构

 view.createLoaderForCategory(categoryId) 

这样的代码

 Loader loader = new ArticleCatalogLoader(context, categoryId); 

导致无法testing的代码。 你应该避免在你的代码中创build“商业”对象,并让其他人为你做(任何DI框架,比如Dagger 2将是比自己更好的select)

话虽如此,你的问题是很久以前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; } }