简体   繁体   English

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

[英]Dagger 2 with MVP, avoid creating extra presenter object on view recreation

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 ). 我实现了MVP模式的应用程序Loader ,以保持视图娱乐演示对象(有关于这个的文章在这里 )。 I'm new to Dagger 2, trying to implement it together with the current code. 我是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中初始化的工厂类创建的,但是随后,在添加Dagger 2实现时,我创建了两个对象(在工厂类中和注入时)。

Now I avoid creating a new presenter in onCreateLoader and passes the injected presenter instead. 现在,我避免在onCreateLoader创建新的演示者,而是传递注入的演示者。 The problem is on view recreation: each time the view is destroyed and recreated, a new presenter is injected in OnCreate / OnCreateView . 问题在于视图重新创建:每次销毁并重新创建视图时,都会在OnCreate / OnCreateView注入一个新的演示者。 This is the scenario: 这是方案:

  1. A new presenter is injected: 注入了一个新的演示者:

     @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { ... getControllerComponent().inject(this); ... } 
  2. Initializing Loader , onCreateLoader is called if the Loader doesn't exist. 如果Loader不存在,则初始化Loader时将调用onCreateLoader 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; } 
  3. Assigning the presenter received from the Loader . 分配从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 . 但是,如果重新创建了视图,则Dagger 2将注入一个新的演示者,在这里,我们将丢弃新的演示者,并用Loader的旧演示者替换它。

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

    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. 我的问题是在每个视图娱乐上创建一个冗余的presenter对象。 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 experts, is there a way to create the presenter the first time (before Loader is created) but avoid it on view recreation? Dagger 2专家,是否有一种方法(在创建Loader之前)首次创建演示者,但避免在视图娱乐时使用它? 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. 首先,我只想提及一下,如果您注入演示者,则以后不应再从加载程序中对其进行分配。

Either use injection to provide an object, or provide it yourself. 可以使用注入来提供对象,也可以自己提供。 If you do both, you are just at risk to introduce bugs. 如果两者都做,则很容易引入错误。


is there a way to create the presenter the first time (before Loader is created) but avoid it on view recreation? 有没有一种方法可以在首次创建演示者时(在创建Loader之前)但是避免在视图重新创建时演示者?

tl;dr You need a scope for your presenter that reflects the lifetime of your component which might survive configuration changes. tl; dr 您需要为演示者提供一个范围,该范围反映了可能在配置更改后仍然存在的组件的生存期。 This scope must not keep any reference to the Activitys Context . 该范围不得保留对Activity Context任何引用。

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. 你通常有一些@Singleton你保持你的内标注的组件Application和一些@PerActivity你每创建一个或类似范围的组件Activity ,这将(也应该)当活动经过配置更改,重新创建因为这些依赖常常引用活动上下文,应该与活动息息相关。

The primary problem you face here is a scoping problem. 您在这里面临的主要问题是范围界定问题。

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范围限定。


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. 如果您的演示者是@PerActivity范围的一部分,则应该(与所有其他@PerActivity依赖项一样)与所有其他依赖项一起重新创建。 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. 同一作用域中的对象可以相互引用,因此,使一个作用域对象在其作用域之外仍处于活动状态也将无意中导致错误,内存泄漏等。

So you don't want to keep this presenter in your Loader either. 因此,您也不想将此演示者保留在Loader中。


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. 另一方面,如果您说“ 不”,那么该演示者在@PerScreen的层次结构中要@PerScreen ,我在其中保留了更长的活动对象,那么您需要找到一种方法来在@PerScreen组件时使该@PerScreen组件保持活动@PerActivity将与活动一起重新创建。

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

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. 当配置发生变化时,你现在可以放弃所有的@PerActivity对象并重新创建它们,同时保持引用您的@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组件而不保留演示者,这是这里的重要部分:

On the @PerScreen scoped component, calling @PerScreen范围内的组件上,调用

myPerScreenComponent.getMyPresenter()

will always return the same @PerScreen scoped presenter. 将始终返回相同的 @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. 现在,如果您的@PerActivty范围内的组件是@PerActivty的子组件, MyPerScreenComponent注入您的活动将始终为您提供相同的@PerScreen范围内的演示者,这将在方向改变后仍然有效。

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). 为了防止内存泄漏, @PerScreen范围内的任何对象都不能引用Activity,并且演示者应仅在其视图上保留WeakReference (否则,请务必确保在destroy时将视图设置为null )。

That's what scopes are for and that's how you avoid creating extra presenter object on view recreation . 这就是作用域的用途,这就是避免在视图娱乐上创建额外的presenter对象的方式

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. 因此,与其将演示者保持在加载器中,还不如将组件保持在加载器中,以避免不必要的对象重现。


All of this will probably introduce a lot more complexity since you now have 2 scopes per activity and more callbacks. 所有这些都可能会带来更多的复杂性,因为您现在每个活动有2个作用域和更多的回调。

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. 我还看到了其他方法,可以在您的应用程序中将演示者保留为单例,但这会带来相同的问题,您必须确保不保留对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. 就个人而言,我只是重新创建演示者并恢复状态,但是如果您选择采用自己的方法,则应确保您对范围和依赖项有深入的了解。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM