我目前正在尝试在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);
}
}
答案 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更长。
您可能有类似Eventbus
或UserRepository
或UserController
的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组件,那么具有生命周期意识的组件将取消订阅视图并为您停止线程。