Mvp 框架 中 v层与p层之间如何防止内存泄漏?(面试题)
1、内存泄漏原因
内存泄漏就是当 GC 来回收时,然而这些对象没有得到释放,或者被其它的给引用了,这种情况下 GC 是不会自动回收的,而这块内存就会被一直占用着,得不到释放,这就是内存泄漏的基本概念。然而,程序这样的情况越来越多时,程序就会出现卡顿、奔溃、报异常的现象,当这些对象占用的内存达到一定的临界值时,机器中没有多余可用的内存,这时你再去申请内存空间,就会发生 OOM (内存溢出)。所以说,内存泄漏是一种安全隐患,它直接影响的是程序的性能。要想在这方面做的好,这需要一个深入的研究。
1、Mvp项目中
BaseMVP 其实也是会造成内存泄漏的一大安全隐患,它的内存泄漏是来自于 View 层与 Presenter 层之间的强引用关系。我们在 Presenter 层直接绑定了 View 才可以拿到 View 层的引用,它们之间是强引用的关系,如果不进行解绑的话,那就会造成内存泄漏的情况发生。为什么不解绑就会内存泄漏呢?我们来看看代码:
(不是最终版代码)
public abstract class BasePresenter<V extends IBaseView> implements IBasePresenter {
protected V mView;
@SuppressWarnings("unchecked")
@Override
public void attech(IBaseView view) {
mView = (V) view;
}
@Override
public void detech() {
// mView = null;
}
}
这里,我们不解绑 View,也就是 mView = null 注释掉,意味着 Presenter 层还持有 View 的引用,当 Activity 被关闭时,Activity 相当于 View 层,由于 Activity 还是被 Presenter 层引用了,当 GC 来了,它一看 Activity 被引用了,所以就不会去回收它。当你再次打开 Activity 又关闭时,Activity 又申请了一段新的内存空间,GC 又没去回收它,久而久之,势必会内存溢出。
而这里置空了就不会造成内存泄漏,因为此时的 View,也就是 Activity 的引用被释放了,如果再也没有其他类引用到 Activity 对象的时候,当 GC 来时,发现 Activity 是可以回收的,就把它回收掉了,这段内存空间就释放了。
说了这么多,其实就是为了介绍我们自己写的 BaseMVP 存在的内存泄漏问题,这里的代码还是基于上一篇 MVP v2 版本进行修改,因为 V 与 P 之间是强引用,
所以我们就改为弱引用用的方式,避免内存泄漏导致的 OOM 情况发生。
(此为kotin代码)
open class BasePresenter<V: IBaseView>:
IPresenter<V> {
protected var mView:V? = null
private var weakReference:WeakReference<V>? = null
protected var compositeDisposable:CompositeDisposable? = null
override fun attachView(view: V) {
weakReference = WeakReference(view)
mView = weakReference!!.get()
}
protected fun addSubscribe(disposable:Disposable){
if(compositeDisposable == null) compositeDisposable = CompositeDisposable()
compositeDisposable!!.add(disposable)
}
protected fun unSubscribe(){
if(compositeDisposable != null){
compositeDisposable!!.clear()
}
}
override fun detachView() {
this.mView = null
}
}
使用弱引用的方式让 P 层持有 V 层的引用,并且提供了 get() 方法给 P 层调用,父类 View 变量进行私有化,防止子类对其进行更改造成的其他错误。我们的 MainPresenter 获取 Activity 的引用就可以使用 get() 方法获得。弱引用在内存降到不足的情况下,GC 就会进行优先回收释放那些以弱引用方式引用的对象,一定程度上去避免内存溢出(OOM)。
动态代理
每次都要让 View 做空判断,很烦?
为什么要用动态代理呢?我们看上面的 get()代码,没次都需要判断 null 类型,是不是非常麻烦,又因为这里的 View 类型是一个接口(V extends IBaseView)泛型接口,所以这就好办了,动态代理完全就可以做到统一的空类型判断。
使用动态代理之后,我们在 Presenter 的实现类中就不需要做 View 层的空类型判断了,这样既节省了代码,虽然没有多少代码,但是写起来还是很烦的,又让我们的代码变得更加优雅。