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

标签 java android mvp loader dagger-2

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

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

现在我避免在 onCreateLoader 中创建新的演示者而是通过注入(inject)的演示者。问题在于 View 重新创建:每次 View 被销毁和重新创建时,都会在 OnCreate 中注入(inject)一个新的演示者。/OnCreateView .这是场景:

  • 注入(inject)了一个新的演示者:
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        ...
        getControllerComponent().inject(this);
        ...
    }
    
  • 初始化 Loader , onCreateLoader如果 Loader 则调用不存在。请注意,我们传递了被注入(inject)的演示者:
    @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;
    }
    
  • 分配从 Loader 接收的演示者.如果它刚刚创建,我们会分配已经分配的相同对象,因此不会发生任何事情。但是,如果重新创建了 View ,那么 Dagger 2 会注入(inject)一个新的 Presenter,在这里我们丢弃新的 Presenter 并用 Loader 中的旧 Presenter 替换它。 .
    @Override
    public void onLoadFinished(Loader<MyPresenter> loader, MyPresenter data) {
        this.presenter = data;
    }
    

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

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

    最佳答案

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

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

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



    tl;博士您需要一个范围来反射(reflect)您的组件的生命周期,该生命周期可能会在配置更改后继续存在。此范围不得保留对 Activity Context 的任何引用.

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

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

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

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

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

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

    另一方面,如果您说不,则该演示者在层次结构中更高一级,是 @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()
    

    将始终返回相同的 @PerScreen范围演示者。

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

    为防止内存泄漏,@PerScreen 中没有任何对象scope 可以引用 Activity,presenter 应该只保留一个 WeakReference在它的 View 上(或者你必须确保在销毁时将 View 设置为 null)。

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

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

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

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

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

    关于java - 带有 MVP 的 Dagger 2,避免在 View 重新创建时创建额外的演示者对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44397877/

    相关文章:

    c# - 两个相同类型的对象的 InvalidCastException

    android - 实现 MVP 时,在 Android 中保留 Presenter 的最佳做法是什么?

    java - 始终在重载构造函数之后执行一些代码

    java - 从单个按钮的多个 fragment 中提取数据

    android - 二进制 XML 文件行 #8 : Error inflating class fragment, Google map

    android - 我可以改变许多开关

    android - Dagger 2 : Inject child class based on logic

    java - 使用 jquery 将参数传递给 Controller

    java - 我们继承自 Object 吗?

    java - Android:如何在布局已经包含按钮的情况下以编程方式覆盖整个布局?