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?

  • MVP是View还是Presenter的onClick责任?
  • Android MVP - 如何在活动演示者和片段演示者之间进行通信
  • Android MVP - 应避免在演示者中使用R.string引用吗?
  • 在Android MVP中放置BroadcastReceiver的位置?
  • Android MVP解释
  • 我可以注册MVP Presenter里面的片段
  • 如何在没有Dagger的MVP中使用共享首选项并且不会导致Presenter依赖于上下文?
  • 在MVP中是View或Presenter的onClick责任吗?
  • 向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; } }