带有MVP的Dagger 2,避免在视图重新创建时创建额外的主持人对象

我有一个应用程序实现MVP模式与一个Loader来维护视图娱乐的presenter对象(这里有一篇关于此的文章)。 我是Dagger 2的新手,试图与当前代码一起实现它。

我已经设法让它工作,但现在我的演示者创建了两次。 起初它是使用在onCreateLoader中初始化的工厂类创建的,但是当添加Dagger 2实现时,我创建了两个对象(在工厂类和注入时)。

现在我避免在onCreateLoader创建一个新的演示者,而是传递注入的演示者。 问题在于视图重新创建:每次销毁和重新创建视图时,都会在OnCreate / OnCreateView注入一个新的演示者。 这是场景:

  1. 注入一个新的演示者:

     @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { ... getControllerComponent().inject(this); ... } 
  2. 初始化Loader ,如果Loader不存在,则调用onCreateLoader 。 请注意,我们传递了注入的演示者:

     @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); getLoaderManager().initLoader(PRESENTER_LOADER_ID, null, this); } @Override public Loader onCreateLoader(int id, Bundle args) { switch (id) { case PRESENTER_LOADER_ID: return new PresenterLoader(getContext(), presenter); //return new PresenterLoader(getContext(), new MyPresenterFactory()); } return null; } 
  3. 分配从Loader收到的演示者。 如果它刚刚创建,我们分配已经分配的相同对象,所以没有任何反应。 但是,如果视图被重新创建,那么Dagger 2会注入一个新的演示者,在这里我们扔掉新的演示者并用Loader的旧演示者替换它。

     @Override public void onLoadFinished(Loader loader, MyPresenter data) { this.presenter = data; } 

    我想维护演示者实例,这就是我想要发生的事情。 我的问题是在每个视图重新创建一个冗余的presenter对象。 首先,它是不必要的,此外,视图持有对不同演示者的引用,直到加载完成。 显然我在这段时间内没有使用演示者(在注入之后和负载完成之前),但我绝对不喜欢它,并担心这个新的演示者将来会被错误地使用。

Dagger 2专家,有没有一种方法可以在第一次创建演示者时(在Loader创建之前)但在观看娱乐时避免使用它? 非常感谢!

首先,我只想提一下,如果你注入了演示者,你不应该在以后从你的装载机再次分配它。

使用注入来提供对象,或者自己提供。 如果你同时做这两件事,你就有可能引入bug。


有没有办法第一次创建演示者(在Loader创建之前),但在视图娱乐时避免它?

tl; dr您需要一个演示者的作用域,它反映了组件的生命周期,这可能会在配置更改后继续存在。 此范围不得保留对活动Context任何引用。

组件遵循一些生命周期。 您通常会在Application保留一些@PerActivity注释组件,以及您为每个Activity创建的一些@PerActivity或类似范围的组件,这些组件将在(或应该)在活动进行配置更改时重新创建,因为这些依赖项通常引用活动上下文应该与Activity一起生存和死亡。

您在这里面临的主要问题是范围问题。

如果您的演示者没有范围,您将在每次请求演示者时重新创建一个新的演示者,只要您将其注入其他地方,就会无意中导致错误。 通常,演示者保留在活动中,并且通常由某些@PerActivity范围@PerActivity范围。


如果您的演示者是@PerActivity范围的一部分,它应该(与所有其他@PerActivity依赖关系一样)与所有其他依赖关系一起重新创建。 如果保留演示者,但重新创建所有其他对象,旧演示者仍将引用旧的依赖项,从而造成内存泄漏。 Scoped对象应该只存在于自己的范围内。

同一作用域中的对象可以相互引用,从而使一个作用域对象在其作用域之外保持活动也会无意中导致错误,内存泄漏等。

因此,您不希望将此演示者保留在Loader中。


另一方面,如果你说No,那个演示者在层次结构中高出一步,是@PerScreen一部分,在那里我保留了更长的生活对象,那么你需要找到一种方法来实际保持这个@PerScreen组件在@PerActivity组件中保持活动@PerActivity将与活动一起重新创建。

假设以下范围层次结构:

 `X > Y` read X is subcomponent of Y @Singleton > @PerScreen > @PerActivity @Singleton: Application wide @PerScreen: Multiple activty lifecycles, keep alive during config changes @PerActivity: Maybe Context dependent, recreate with every activity 

当发生配置更改时,您现在可以丢弃所有@PerActivity对象并重新创建它们,同时保留对@PerActivity对象的引用。

您可能会注意到我如何继续讨论保留@PerScreen组件而不是保留演示者,这是重要的部分:

@PerScreen作用域组件上,调用

 myPerScreenComponent.getMyPresenter() 

将始终返回相同的 @PerScreen作用域演示者。

现在,如果您的@PerActivty范围组件是@PerActivty的子组件,则注入您的活动将始终为您提供相同的@PerScreen范围的演示者,该演示者将在方向更改后继续存在。

为了防止内存泄漏, @PerScreen作用域中的任何对象都不能引用Activity,并且演示者应该只在其视图上保留WeakReference (或者你必须确保在destroy上将视图设置为null )。

这就是范围的目的,这就是你如何避免在视图重新创建时创建额外的presenter对象

因此,您应该尝试将组件保留在加载程序中,以避免不必要地重新创建对象,而不是将您的演示者保留在加载程序中。


所有这些可能会引入更多复杂性,因为您现在每个活动有2个范围和更多回调。

我还看到了其他方法,您可以在演示文稿中将演示者作为单身人士,但这会引入相同的问题,您必须确保不要保留对您的活动的任何引用。

就个人而言,我只是重新创建演示者并恢复状态,但如果您选择使用您的方法,则应确保您对范围和依赖关系有充分的了解。