当前位置: 首页 > 面试题库 >

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

满勇军
2023-03-14
问题内容

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

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

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

  1. 注入了一个新的演示者:
        @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        ...
        getControllerComponent().inject(this);
        ...
    }
  1. 如果不存在LoaderonCreateLoader则调用Initializing Loader。请注意,我们通过了被注入的主持人:
        @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;
    }
  1. 分配从接收的演示者Loader。如果是刚刚创建的,我们将分配与已分配的对象相同的对象,因此不会发生任何事情。但是,如果重新创建了视图,则Dagger 2注入了一个新的演示者,在这里我们丢弃了该新的演示者,并将其替换为的旧的演示者Loader
        @Override
    public void onLoadFinished(Loader<MyPresenter> loader, MyPresenter data) {
        this.presenter = data;
    }

我想维护演示者实例,所以这就是我想发生的事情;我的问题是在每个视图娱乐上创建一个冗余的presenter对象。首先,这是不必要的,此外,视图将保持对其他演示者的引用,直到完成加载为止。显然,在此期间(注射后和负载完成之前),我不使用演示者,但是我绝对不喜欢它,并且担心将来会错误地使用这个新的演示者。

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


问题答案:

首先,我只想提及一下,如果您注入演示者,则以后不应再从加载程序中对其进行分配。

可以使用注入来提供对象,也可以自己提供。如果两者都做,则很容易引入错误。

有没有一种方法可以在首次创建演示者时(在创建Loader之前)但在视图重新创建时避免它?

tl; dr
您需要为演示者提供一个范围,该范围反映了可能在配置更改后仍然存在的组件的生存期。此范围不得保留对Activity的任何引用Context

组件遵循某些生命周期。通常@Singleton,您会保留一些带注释的组件,Application并在@PerActivity每个组件中创建一些或类似作用域的组件,Activity当活动进行配置更改时,这些组件将(并且应该)重新创建,因为这些依赖项通常引用Activity上下文,并且应该存在并且死于活动。

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

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

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

同一作用域中的对象可以互相引用,因此使一个作用域对象在其作用域之外仍然有效,也会无意中导致错误,内存泄漏等。

因此,您也不想 将此 演示者保留在Loader中。

另一方面,如果您说“
_否”,那么演示者在层次结构中要高出一步,该层次结构是@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()

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

现在,如果您的@PerActivty作用域组件是的子组件MyPerScreenComponent,那么注入您的活动将始终为您提供相同的@PerScreen作用域演示者,这将在方向改变后仍然有效。

为了防止内存泄漏,您@PerScreen范围内的任何对象都不能引用该活动,并且演示者仅应保留WeakReference其视图(否则,请务必确保将视图设置为null“销毁”)。

这就是作用域的用途,这就是 避免在视图娱乐上创建额外的presenter对象的方式

因此,与其将演示者保留在加载器中,不如将组件保留在加载器中,以避免不必要的对象重现。

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

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

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



 类似资料:
  • 问题内容: 我正在多线程环境中聚合键的多个值。密钥未知。我以为我会做这样的事情: 我看到的问题是,每次运行此方法时,我都需要创建一个新的实例,然后将其丢弃(在大多数情况下)。这似乎是对垃圾收集器的不合理滥用。是否有更好的,线程安全的方法来初始化这种结构而不必使用该方法?我对使该方法不返回新创建的元素的决定感到惊讶,并且对缺少除非被要求(可以这么说)实例化实例的延迟的方法感到惊讶。 问题答案: Ja

  • 我正在进行一项活动,其中有TabLayout和两个表示选项卡内容的片段。我在活动的OnCreate方法中手动管理打开选项卡的当前状态: 这只是一个简单的例子。我真正的代码是用C语言编写的,并且有一些其他逻辑来存储和管理活动重启之间的状态。 配置更改时的问题(例如开关方向): 打开的碎片被摧毁 活动被破坏 活动重启 在onCreate方法中,当调用super.onCreate(savedInstan

  • 为了更新一些图像,我使用了“docker-compose pull”。然后我构建:'docker-compose build'。 下次我怎么避免这个?

  • 问题内容: 我是Java的新手,并且开始使用Java 。我想做的是为学生创建一个。每个学生都有与其相关的不同属性()。我试图弄清楚如何使用此属性添加新的学生对象。这是我所拥有的: 问题答案: 您需要的是以下内容:

  • 垃圾回收期在管理内存方面非常出色,它非常高效地移除不再使用的对象。但是无论你如何看待它,分配和销毁一个基于堆内存的对象花费处理器时间比分配和销毁不是基于堆内存的对象要多。在函数内创建大量的引用类型对象会引入严重的性能消耗问题。 所以不能让垃圾回收器超负荷工作。你可以借鉴一些简单的技巧最小化垃圾回收器的工作。所有的引用类型对象,即使是局部变量,都被分配存储在堆内存上。每个引用类型的局部变量在函数结束

  • 我的问题是--如何创建自定义列表视图,而不仅仅是重复一个自定义视图,而是像在Instagram或其他应用程序中,列表包括其他视图,这看起来就像滚动视图和列表视图android其他视图一样,但Roman Guy说“在滚动视图中的列表视图是一种非常糟糕的方式”,我同意这一点,不要相信谷歌使用这种方式... 使用ListView或Recolyer View实现此功能的最佳方法是什么