【问题标题】:Dagger 2 with MVP, avoid creating extra presenter object on view recreation带有 MVP 的 Dagger 2,避免在视图重新创建时创建额外的演示者对象
【发布时间】:2017-11-07 22:26:21
【问题描述】:

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

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

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

  1. 一个新的presenter被注入:

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

    @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 之前)但在视图娱乐时避免它?非常感谢!

【问题讨论】:

  • 你能分享一下匕首组件定义和它所依赖的模块吗?

标签: java android mvp loader dagger-2


【解决方案1】:

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

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


有没有办法在第一次创建演示者(在创建加载器之前)但在视图重新创建时避免它?

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

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

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

如果您的演示者没有作用域,您将在每次请求时重新创建一个新演示者,这会在您将其注入其他地方时无意中导致错误。通常情况下,演示者被保留在活动中,并且通常由一些 @PerActivity 范围限定。


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

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

所以你也不想在你的加载器中保留这个演示者。


另一方面,如果你说 不,那个演示者在层次结构中更高一级,是 @PerScreen 的一部分,我在那里保存更长的生命对象那么你需要找到一种方法来真正保持这个@PerScreen 组件处于活动状态,而您的@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 对象并重新创建它们,同时保留对您的 @PerScreen 对象的引用。

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

@PerScreen 作用域组件上,调用

myPerScreenComponent.getMyPresenter()

将始终返回 same @PerScreen 作用域演示者。

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

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

这就是作用域的用途,也是您避免在视图重新创建时创建额外的演示者对象的方式

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


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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-05-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-25
    • 1970-01-01
    • 2016-12-21
    相关资源
    最近更新 更多