java - 了解 android MVP 中的漏洞
问题描述
我在这里阅读了一些类似的问题,但由于缺少代码,我不确定我的问题描述了相同的场景。
我希望以下片段和问题将帮助其他人澄清在这个 MVP 实现中泄漏了什么以及何时泄漏:https ://github.com/frogermcs/GithubClient/tree/1bf53a2a36c8a85435e877847b987395e482ab4a
BaseActivity.java:
public abstract class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setupActivityComponent();
}
protected abstract void setupActivityComponent();
}
SplashActivityModule.java:
@Module
public class SplashActivityModule {
private SplashActivity splashActivity;
public SplashActivityModule(SplashActivity splashActivity) {
this.splashActivity = splashActivity;
}
@Provides
@ActivityScope
SplashActivity provideSplashActivity() {
return splashActivity;
}
@Provides
@ActivityScope
SplashActivityPresenter
provideSplashActivityPresenter(Validator validator, UserManager
userManager, HeavyLibraryWrapper heavyLibraryWrapper) {
return new SplashActivityPresenter(splashActivity, validator,
userManager, heavyLibraryWrapper);
}
}
SplashActivityPresenter 被注入到 SplashActivity.java 中:
public class SplashActivity extends BaseActivity {
...
@Inject
SplashActivityPresenter presenter;
@Override
protected void setupActivityComponent() {
GithubClientApplication.get(this)
.getAppComponent()
.plus(new SplashActivityModule(this))
.inject(this);
}
SplashActivityPresenter.java:
public class SplashActivityPresenter {
public String username;
private SplashActivity splashActivity;
private Validator validator;
private UserManager userManager;
private HeavyLibraryWrapper heavyLibraryWrapper;
public SplashActivityPresenter(SplashActivity splashActivity,
Validator validator, UserManager userManager,
HeavyLibraryWrapper heavyLibraryWrapper) {
this.splashActivity = splashActivity;
this.validator = validator;
this.userManager = userManager;
this.heavyLibraryWrapper = heavyLibraryWrapper;
//This calls should be delivered to ExternalLibrary right after it will be initialized
this.heavyLibraryWrapper.callMethod();
this.heavyLibraryWrapper.callMethod();
this.heavyLibraryWrapper.callMethod();
this.heavyLibraryWrapper.callMethod();
}
public void onShowRepositoriesClick() {
if (validator.validUsername(username)) {
splashActivity.showLoading(true);
userManager.getUser(username).subscribe(new
SimpleObserver<User>() {
@Override
public void onNext(User user) {
splashActivity.showLoading(false);
splashActivity.showRepositoriesListForUser(user);
}
@Override
public void onError(Throwable e) {
splashActivity.showLoading(false);
splashActivity.showValidationError();
}
});
} else {
splashActivity.showValidationError();
}
}
}
- 如果用户在获取用户名时旋转屏幕,我们将泄漏在观察者的回调中引用的活动实例,因为活动被破坏了。
- 如果用户在没有进行中获取的情况下旋转屏幕,则活动实例不会泄漏。
- 为了修复这个泄漏(1),我们需要在presenter.onDestroy()中存储订阅和取消订阅(从SplashActivity onDestroy()调用)。
- 有人告诉我,做(3)还不够,里面
onDestroy()
还要把activity instance设置为null
. 我不同意,因为取消订阅会取消请求,从而阻止调用onNext(User)
引用活动的回调(如 )。 - 他还告诉我,虽然 (3) 和 (4) 可以防止 ACTIVITY 泄漏,但 PRESENTER 在轮换期间也会泄漏,因为活动引用了它。我不同意,因为每次旋转都会创建一个新的演示者,并在 BaseActivity onCreate 调用 setupActivityComponent 时将其初始化为注入的演示者。旧的 Presenter 作为 SplashActivity 的成员自动被垃圾收集。
有人可以回应我上面概述的要点,以便我确认我的理解或了解我可能错在哪里吗?谢谢
解决方案
我会尽可能准确地回答这些问题(如果不准确,欢迎编辑),但这个答案很好读:https ://stackoverflow.com/a/10968689/4252352
- 如果用户在获取用户名时旋转屏幕,我们将泄漏在观察者的回调中引用的活动实例,因为活动被破坏了。
A : 这是正确的,但是在处置后资源被清除之前会出现短期内存泄漏。然而,这不应该被依赖,如果你有一个 Flowable/Observable 它可能永远不会处理和清除资源。需要注意的是,Rx 链中的所有不引用(捕获)封闭类的 lambda 表达式(通常是 等运算符)都是无泄漏的map
。filter
- 如果用户在没有进行中获取的情况下旋转屏幕,则活动实例不会泄漏。
A. 正确,您从未有过有效订阅。
- 为了修复这个泄漏(1),我们需要在presenter.onDestroy()中存储订阅和取消订阅(从SplashActivity onDestroy()调用)
A 这应该会停止这个问题。然而更好的方法,在 MVP 中View
应该是一个抽象/接口,你Presenter
应该有视图的入口和出口点,而不是在构造函数上,即这里bind(view : View)
的unbind()
<--cleanup,演示者不应该知道特定的 android hook 回调. 这具有巨大的优势,不仅从 OOP(程序到接口而不是实现)方面,而且在测试方面。
- 有人告诉我,做 (3) 是不够的,在 onDestroy() 内部我们还必须将活动实例设置为 null。我不同意,因为取消订阅会取消请求,从而阻止调用引用活动的回调(如 onNext(User))。
答:我会首先要求澄清他们的推理。由于您Presenter
的范围为Activity
(它们都具有相同的生命周期),因此取消订阅就足够了。但是,如果您Presenter
的生命周期比您的生命周期长,Activity
则有必要删除引用(这可能是您与之交谈的人的基本原理)。
- 他还告诉我,虽然 (3) 和 (4) 可以防止 ACTIVITY 泄漏,但 PRESENTER 在轮换期间也会泄漏,因为活动引用了它。我不同意,因为每次旋转都会创建一个新的演示者,并在 BaseActivity onCreate 调用 setupActivityComponent 时将其初始化为注入的演示者。旧的 Presenter 作为 SplashActivity 的成员自动被垃圾收集。
A.Presenter
被泄露,如果Activity
被泄露(连同活动引用的所有内容!)
推荐阅读
- python - 为 pandas 列创建类别计数字典
- jenkins - 在下游作业中聚合多个上游 SCM 更改
- java - 神经元结果有点偏离
- kubernetes - 如何在 minikube 上启用审核日志?
- php - 长时间运行的 Cronjob php 脚本停止而没有错误
- angular - 从 defer 中返回 shareReplay 有意义吗?
- python - 找不到我的模块
- php - file_get_contents 中的二进制文件会发生什么?
- objective-c - 在 Objective-C 中按名称创建一个 Swift 类
- python - 在 python 3 中,字符串输入 'python' &python 的区别