我有一个应用程序使用 Loader
实现 MVP 模式在 View 娱乐中维护演示者对象(有一篇关于此的文章 here )。我是 Dagger 2 的新手,试图将它与当前代码一起实现。
我已经设法让它工作,但现在我的演示者被创建了两次。起初它是使用在 onCreateLoader
中初始化的工厂类创建的。 ,但是,当添加 Dagger 2 实现时,我创建了两个对象(在工厂类和注入(inject)时)。
现在我避免在 onCreateLoader
中创建新的演示者而是通过注入(inject)的演示者。问题在于 View 重新创建:每次 View 被销毁和重新创建时,都会在 OnCreate
中注入(inject)一个新的演示者。/OnCreateView
.这是场景:
@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/