MVP,在销毁时将演示者的视图设置为null?

时间:2016-12-18 00:53:42

标签: android mvp dagger-2

我目前正在尝试在Android上实现MVP模式。但是,我开始考虑内存泄漏(因为演示者持有对活动的引用 - 视图)。我的问题是,我应该将演示者的视图设置为null表示活动的onDestroy吗?

这是我的主要活动:

public class MainActivity extends AppCompatActivity implements MainView {
private Button loadUser;
private TextView mTextView;
@Inject
IMainPresenter mPresenter;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    setUpViews();

    ((MyApp) getApplication()).getAppComponent().inject(this);
    mPresenter.setView(this);
}

private void setUpViews() {
    loadUser = (Button) findViewById(R.id.getUserBtn);
    loadUser.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            mPresenter.loadUserName(2);
        }
    });
    mTextView = (TextView) findViewById(R.id.userNameTextView);
}

@Override
public void setUserName(String userName) {
    mTextView.setText(userName);
}

@Override
protected void onDestroy() {
    super.onDestroy();
    mPresenter.setView(null);
}
}

5 个答案:

答案 0 :(得分:4)

我假设您使用的是注入库(可能是Dagger查看您的代码),Presenter注释了@Singleton?如果是这样,那么设置为null是一个选项(是的,您不应该在配置更改上保留Activity实例)。

另一种选择是使用WeakReference中的Presenter,这样设置为null不是必需的,尽管设置为null更明确。

您可以考虑将接口与Presenter一起使用,而不是将整个Activity暴露给Presenter实例 - 您可能已经做过类似的事情,但不能100%清除提供的代码。

答案 1 :(得分:2)

我总是喜欢这样,我的意思是,我总是在onDestroy()方法上将视图设置为null。我尝试使用LightCycle库来避免编写所有样板代码。这篇文章解释了Fernando Ceja的Clean Architecture。这是一个广泛使用的MVP架构(我曾在几家使用此模式的公司工作过),在那里你可以看到他还在onDestroy()方法中将视图设置为null。如果我能帮助你,请告诉我。

<强>更新

这个答案有点过时,现在您可以使用Android的Jetpack工具中的LifecycleOwner类。它基本上是相同的交易,但具有不同的API。

答案 2 :(得分:2)

您是否可以使用https://developer.android.com/topic/libraries/architecture/lifecycle.html识别演示者生命周期?然后,当在视图上调用onDestroy()时,演示者可以自己负责将视图设置为null。

答案 3 :(得分:0)

如果您未将view(Activity)设置为null,那么您的演示者将继续持有对Activity的引用。 我将以“活动”为例,但“片段”也可以使用。

这是一个问题,为什么?

这取决于演示者的生命周期。您需要了解演示者在内存中保留的时间。

案例1:

如果您的演示者定义为Singleton(我认为您不应该这样做),那么它会保留在内存中,直到应用程序本身为止。

这是一个问题,因为这样一个单例类将包含对Activity的引用,因此可以防止对其进行垃圾回收。当您的活动被销毁时,对用户来说将是无用的,但仍会占用内存(RAM)。除占用宝贵的内存外,演示者还可能会收到成功的API响应之类的事件,这将触发view.doSomething方法,这可能会导致崩溃(使用被破坏的Activity用户界面不是您应该做的事情,Android框架会抛出例外)。

通常,Presenter的范围不那么广泛,所以这不是问题。如果您的演示者的生存期比活动寿命长,则需要将视图引用手动设置为null才能解决此问题。

案例2:

您的Presenter可能没有被定义为Singleton,但是其他某些Singleton或类似的范围广泛的组件可能持有对Presenter的引用,从而使其寿命比Activity更长。

您可能有类似EventbusUserRepositoryUserController的Singleton类。您的演示者可能持有对该对象的引用并被订阅。订阅时,基本上是在设置自己对其他课程的引用。在Presenter中,当您呼叫eventbus.subscribe(this)userRepository.setListener(this)时,您是在向Singleton提供对您自己的引用。单例对象拥有对您对象的引用,只要单例对象存在,该对象就会在内存中存储,这是永远的。 如果您使用的是lambda,匿名类或嵌套类,则此类构造将包含对其封闭类的引用,这可能会导致以下引用链: Singleton->Anonymous class->Presenter->Activity

在这种情况下,Singleton间接引用了一个Activity,以防止其被垃圾回收!

您可能使用的是这样的内容,而不是userRepository.setListener(this)

userRepository.updateUser(object: Listener {
   onSuccess() {
      // do something
   }
   onFailure() {}
})

比方说,updateUser方法执行较长的操作(发送API请求)。在这种情况下,仅在更新用户请求正在进行时保留参考。调用onSuccess或onFailure时,通常(实现可能有所不同)被清除。因此,这里的内存泄漏问题并不是什么大问题,因为将清除引用,只是稍有延迟。但是,仍然存在在销毁视图后更新视图的问题(在案例1中进行了描述)-您可以通过在请求进行时退出活动来重现视图。

要解决上述问题,您需要使用userRepository.clear()eventbus.unsubscribe(this)之类的东西。此类方法的实现应取消所有进行中的请求,并删除对订户/侦听器(演示者)的引用。 如果操作正确,您甚至不需要将演示者的视图引用设置为null。垃圾收集器足够聪明,可以弄清楚您的Activity和Presenter对象是否相互引用,但是没有其他组件对它们保持引用并将这两个对象从“内存”中删除。 但是,我的建议是仍然执行此操作,因为它是一个简单的操作,不必在任何地方重复(可以在BasePresenter类中完成),并且如果您忘记手动清除一些引用,可以节省您的时间。

答案 4 :(得分:0)

方案1。

如果Presenter的生命周期不比Activity的生命周期长,则无需将view设置为null。重要的部分是停止演示者执行工作的后台线程。如果您在onDestroy中停止Presenter的后台线程,则该线程将终止并且不再保持指向Activity的链接。因此,Activity和Presenter都可以很快被GC收集。

这种情况通常可以在使用MVP且不处理方向更改的项目中找到。

方案2。

如果Presenter的寿命比Activity(例如单身人士)的寿命长,则应将View设置为null。描述了Here cases like that

如果您希望演示者的线程在配置更改后继续存在,则很可能使演示者的生存期比活动的生存期更长。


A建议您切换到MVVM而不是MVP。如果您以正确的方式使用Google Architecture组件,那么具有生命周期意识的组件将取消订阅视图并为您停止线程。