带有MVP的Dagger 2,避免在视图重新创建时创建额外的主持人对象 [英] Dagger 2 with MVP, avoid creating extra presenter object on view recreation

查看:127
本文介绍了带有MVP的Dagger 2,避免在视图重新创建时创建额外的主持人对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个用 Loader 实现MVP模式的应用程序来维护视图娱乐的presenter对象(有一篇关于这个的文章这里)。我是Dagger 2的新手,试图将它与当前代码一起实现。

I have an app implementing the MVP pattern with a Loader to maintain the presenter object on view recreation (there's an article about this here). I'm new to Dagger 2, trying to implement it together with the current code.

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

I've managed to make it work, but now my presenter is created twice. At first it was created using a factory class which was initialized in onCreateLoader, but then, when adding Dagger 2 implementation, I had two objects created (in factory class and when injecting).

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

Now I avoid creating a new presenter in onCreateLoader and passes the injected presenter instead. The problem is on view recreation: each time the view is destroyed and recreated, a new presenter is injected in OnCreate / OnCreateView. This is the scenario:


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

  1. A new presenter is injected:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    ...
    getControllerComponent().inject(this);
    ...
}


  • 初始化 Loader 不存在,则调用 onCreateLoader 。请注意,我们传递了注入的演示者:

  • Initializing Loader, onCreateLoader is called if the Loader doesn't exist. Note that we pass the presenter that was injected:

    @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收到的演示者。如果它刚刚创建,我们分配已经分配的相同对象,所以没有任何反应。但是,如果视图被重新创建,那么Dagger 2会注入一个新的演示者,在这里我们扔掉新的演示者并用 Loader 中的旧演示者替换它。

  • Assigning the presenter received from the Loader. If it was just created, we assign the same object that's already assigned so nothing happens. But, if the view was recreated, then Dagger 2 injected a new presenter and here we throw away the new presenter and replace it with the old presenter from the Loader.

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

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

    I want to maintain the presenter instance so it's ± what I want to happen; my problem is with creating a redundant presenter object on each view recreation. First, it's unnecessary, and additionally, the view is holding a reference to a different presenter until the load is finished. Obviously I am not using the presenter during this period (after injection and before the load is finished), but I definitely don't like it and afraid that this new presenter will be mistakenly used in the future.

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

    Dagger 2 experts, is there a way to create the presenter the first time (before Loader is created) but avoid it on view recreation? Thanks a lot!

    推荐答案

    首先我想提一下,如果您注入演示者,您不应该在以后再分配它来自您的装载机。

    First off I just want to mention that if you inject your presenter, you should not assign it again later from your loader.

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

    Either use injection to provide an object, or provide it yourself. If you do both, you are just at risk to introduce bugs.


    是否有第一次创建演示者的方法(在Loader创建之前),但在视图娱乐时避免它?

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

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

    tl;dr You need a scope for your presenter that reflects the lifetime of your component which might survive configuration changes. This scope must not keep any reference to the Activitys Context.

    组件遵循某些生命周期。你通常有一些 @Singleton 带注释的组件,你保存在应用程序和一些 @PerActivity中或您为 Activity 创建的类似范围的组件,这些组件将在(或应该)在活动进行配置更改时重新创建,因为这些依赖项通常是引用Activity上下文,并且应该使用Activity来生存和死亡。

    Components follow some lifecycle. You usually have some @Singleton annotated component that you keep within your Application and some @PerActivity or similarly scoped components that you create per Activity, which will (and should) be recreated when an activity goes through configuration changes, since those dependencies often reference the Activity context and should live and die with the Activity.

    这里面临的主要问题是范围问题。

    The primary problem you face here is a scoping problem.

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

    If your presenter is unscoped, you will recreate a new presenter every time you request one, which will inadvertently lead to bugs, as soon as you inject it somewhere else. Usually presenters are kept within the activity and often scoped by some @PerActivity scope.

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

    If your presenter is part of the @PerActivity scope it should (like all the other @PerActivity dependencies) be recreated along with all the other dependencies. If you keep the presenter, but recreate all the other objects, the old presenter will still reference the old dependencies, creating memory leaks. Scoped objects should just exist within their own scope.

    同一范围内的对象可以互相引用,从而使一个范围内的对象保持活跃它的范围也会无意中导致错误,内存泄漏等。

    Objects in the same scope can reference one another and thus keeping one scoped object alive outside of its scope will also inadvertently lead to bugs, memory leaks, etc.

    所以你不想在你的Loader中保留这个演示者。

    So you don't want to keep this presenter in your Loader either.

    如果另一方面你说 No,那么该演示者在层次结构中高出一步,部分 @PerScreen ,我保留更长的生活对象然后你需要找到一种方法来实际保持这个 @PerScreen 组件存活,而 @PerActivity 组件将与活动一起重新创建。

    If on the other hand you say No, that presenter is one step higher in the hierarchy, part of @PerScreen, where I keep longer living objects then you need to find a way to actually keep this @PerScreen component alive while your @PerActivity component will be recreated along with the activity.

    假设以下范围层次结构:

    Assume the following scope hierarchy:

    `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 的引用。

    When a configuration change occurs you now can discard all of the @PerActivity objects and recreate them, while keeping the reference to your @PerScreen ones.

    你可能会注意到我是怎样的继续谈论保持 @PerScreen 组件保留演示者,这是重要的部分:

    You might notice how I keep talking about keeping the @PerScreen component, not keeping the presenter, and that is the important part here:

    @PerScreen 作用域组件上,调用

    myPerScreenComponent.getMyPresenter()
    

    将始终返回相同的 @PerScreen 作用域的演示者。

    will always return the same @PerScreen scoped presenter.

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

    Now if your @PerActivty scoped component is a subcomponent of MyPerScreenComponent, injecting your activity will always give you the same @PerScreen scoped presenter, which will survive orientation changes.

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

    To prevent memory leaks, no object from your @PerScreen scope can reference the Activity, and the presenter should only keep a WeakReference on its view (or you have to make damn sure to set the view to null on destroy).

    这就是范围的目的,这就是你如何避免在视图重新创建上创建额外的演示者对象。

    That's what scopes are for and that's how you avoid creating extra presenter object on view recreation.

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

    So instead of keeping your presenter in your loader you should try to keep the component in your loader to avoid unnecessary recreation of your objects.

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

    All of this will probably introduce a lot more complexity since you now have 2 scopes per activity and more callbacks.

    我还看到了其他方法,你可以在你的应用程序中将你的演示者保持为单身人士,但是引入了同样的问题,你必须确保不要保留对你的Activity的任何引用。

    I have also seen other approaches where you keep your presenters as Singletons in your Application, but this introduces the same problems where you have to make sure not to keep any references to your Activity.

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

    Personally, I would just recreate the presenters and restore the state, but if you choose to go with your approach you should make sure that you have a solid understanding of scopes and dependencies.

    这篇关于带有MVP的Dagger 2,避免在视图重新创建时创建额外的主持人对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆