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

Ner*_*hum 1 java mvp android loader dagger-2

我实现了MVP模式的应用程序Loader,以保持对视娱乐演示对象(有关于这个的文章在这里).我是Dagger 2的新手,试图与当前代码一起实现它.

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

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

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

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        ...
        getControllerComponent().inject(this);
        ...
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 初始化Loader,onCreateLoader被称为如果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;
    }
    
    Run Code Online (Sandbox Code Playgroud)
  3. 分配从中收到的演示者Loader.如果它刚刚创建,我们分配已经分配的相同对象,所以没有任何反应.但是,如果视图被重新创建,那么Dagger 2会注入一个新的演示者,在这里我们扔掉新的演示者并用旧的演示者替换它Loader.

    @Override
    public void onLoadFinished(Loader<MyPresenter> loader, MyPresenter data) {
        this.presenter = data;
    }
    
    Run Code Online (Sandbox Code Playgroud)

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

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

Dav*_*jak 10

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

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


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

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

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

您面临的主要问题是范围问题.

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


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

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

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


另一方面,如果你说No,那个演示者在层次结构中高一级@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
Run Code Online (Sandbox Code Playgroud)

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

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

@PerScreen范围组件上,调用

myPerScreenComponent.getMyPresenter()
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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


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

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

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