带有 MVP 的 Dagger 2,避免在视图重新创建时创建额外的演示者对象

Dagger 2 with MVP, avoid creating extra presenter object on view recreation

我有一个使用 Loader 实现 MVP 模式的应用程序以在视图重新创建时维护演示者对象(有一篇关于此 here 的文章)。我是 Dagger 2 的新手,正在尝试将其与当前代码一起实现。

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

现在我避免在 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<MyPresenter> 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 会注入一个新的 Presenter,在这里我们丢弃新的 Presenter 并将其替换为 Loader.

    中的旧 Presenter
    @Override
    public void onLoadFinished(Loader<MyPresenter> loader, MyPresenter data) {
        this.presenter = data;
    }
    

    我想维护演示者实例,所以这就是我想要发生的事情;我的问题是在每次重新创建视图时创建一个冗余的演示者对象。首先,这是不必要的,此外,在加载完成之前,视图一直持有对不同演示者的引用。很明显这段时间(注入后,加载完成前)我没有使用presenter,但是我肯定不喜欢,怕以后误用这个新的presenter

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

首先我只想提一下,如果你注入你的演示者,你不应该稍后从你的加载器中再次分配它。

要么使用注入提供对象,要么自己提供。如果你两者都做,你就有引入错误的风险。


is there a way to create the presenter the first time (before Loader is created) but avoid it on view recreation?

tl;dr 您的 Presenter 需要一个范围来反映您的组件的生命周期,它可能会在配置更改后继续存在。此范围不得保留对 Activitys Context.

的任何引用

组件遵循一些生命周期。您通常会在 Application 中保留一些 @Singleton 带注释的组件,以及您根据 Activity 创建的一些 @PerActivity 或类似范围的组件,这些组件将(并且应该)重新创建当 activity 经历配置更改时,因为这些依赖项通常引用 Activity 上下文,并且应该与 Activity.

一起生存和死亡

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

如果您的 Presenter 是无范围的,您将在每次请求时重新创建一个新的 Presenter,一旦您将它注入其他地方,这将无意中导致错误。通常演示者保持在 activity 范围内,并且通常被某些 @PerActivity 范围限定。


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

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

所以您也不想在加载程序中保留 this 演示者。


如果另一方面你说 不,那个演示者在层次结构中更高一级,是 @PerScreen 的一部分,我在那里保存更长的生命对象 那么你需要找到一种方法来实际保持此 @PerScreen 组件处于活动状态,而您的 @PerActivity 组件将与 activity.

一起重新创建

假定以下范围层次结构:

`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 对象并重新创建它们,同时保留对 @PerScreen 对象的引用。

您可能会注意到我一直在谈论 保留 @PerScreen 组件 而不是 保留演示者,那就是这里的重要部分:

@PerScreen 作用域组件上,调用

myPerScreenComponent.getMyPresenter()

将始终return 相同 @PerScreen 范围内的演示者。

现在,如果您的 @PerActivty 作用域组件是 MyPerScreenComponent 的子组件,注入您的 activity 将始终为您提供相同的 @PerScreen 作用域演示器,它将在定向后继续存在变化。

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

这就是范围的用途,这就是您如何避免在视图重新创建时创建额外的演示者对象

因此,与其将演示者保留在加载器中,不如尝试将组件保留在加载器中,以避免不必要地重新创建对象。


所有这些可能会带来更多的复杂性,因为您现在每个 activity 有 2 个作用域和更多回调。

我还看到了其他方法,您可以在应用程序中将演示者保持为单例,但这会带来同样的问题,您必须确保不保留对 Activity 的任何引用。

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