1邪锌、內(nèi)存泄漏原因
內(nèi)存泄漏就是當(dāng) GC 來回收時(shí)勉躺,然而這些對(duì)象沒有得到釋放,或者被其它的給引用了觅丰,這種情況下 GC 是不會(huì)自動(dòng)回收的饵溅,而這塊內(nèi)存就會(huì)被一直占用著,得不到釋放妇萄,這就是內(nèi)存泄漏的基本概念蜕企。然而咬荷,程序這樣的情況越來越多時(shí),程序就會(huì)出現(xiàn)卡頓轻掩、奔潰萍丐、報(bào)異常的現(xiàn)象,當(dāng)這些對(duì)象占用的內(nèi)存達(dá)到一定的臨界值時(shí)放典,機(jī)器中沒有多余可用的內(nèi)存逝变,這時(shí)你再去申請(qǐng)內(nèi)存空間,就會(huì)發(fā)生 OOM (內(nèi)存溢出)奋构。所以說壳影,內(nèi)存泄漏是一種安全隱患,它直接影響的是程序的性能弥臼。要想在這方面做的好宴咧,這需要一個(gè)深入的研究。
1径缅、Mvp項(xiàng)目中
BaseMVP 其實(shí)也是會(huì)造成內(nèi)存泄漏的一大安全隱患掺栅,它的內(nèi)存泄漏是來自于 View 層與 Presenter 層之間的強(qiáng)引用關(guān)系。我們?cè)?Presenter 層直接綁定了 View 才可以拿到 View 層的引用纳猪,它們之間是強(qiáng)引用的關(guān)系氧卧,如果不進(jìn)行解綁的話,那就會(huì)造成內(nèi)存泄漏的情況發(fā)生氏堤。為什么不解綁就會(huì)內(nèi)存泄漏呢沙绝?我們來看看代碼:
(不是最終版代碼)
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 的引用,當(dāng) Activity 被關(guān)閉時(shí)购笆,Activity 相當(dāng)于 View 層粗悯,由于 Activity 還是被 Presenter 層引用了,當(dāng) GC 來了同欠,它一看 Activity 被引用了样傍,所以就不會(huì)去回收它。當(dāng)你再次打開 Activity 又關(guān)閉時(shí)行您,Activity 又申請(qǐng)了一段新的內(nèi)存空間铭乾,GC 又沒去回收它,久而久之娃循,勢(shì)必會(huì)內(nèi)存溢出炕檩。
而這里置空了就不會(huì)造成內(nèi)存泄漏,因?yàn)榇藭r(shí)的 View,也就是 Activity 的引用被釋放了笛质,如果再也沒有其他類引用到 Activity 對(duì)象的時(shí)候泉沾,當(dāng) GC 來時(shí),發(fā)現(xiàn) Activity 是可以回收的妇押,就把它回收掉了跷究,這段內(nèi)存空間就釋放了。
說了這么多敲霍,其實(shí)就是為了介紹我們自己寫的 BaseMVP 存在的內(nèi)存泄漏問題俊马,這里的代碼還是基于上一篇 MVP v2 版本進(jìn)行修改,因?yàn)?V 與 P 之間是強(qiáng)引用肩杈,
所以我們就改為弱引用用的方式柴我,避免內(nèi)存泄漏導(dǎo)致的 OOM 情況發(fā)生。
(此為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 層調(diào)用艘儒,父類 View 變量進(jìn)行私有化,防止子類對(duì)其進(jìn)行更改造成的其他錯(cuò)誤夫偶。我們的 MainPresenter 獲取 Activity 的引用就可以使用 get() 方法獲得界睁。弱引用在內(nèi)存降到不足的情況下,GC 就會(huì)進(jìn)行優(yōu)先回收釋放那些以弱引用方式引用的對(duì)象兵拢,一定程度上去避免內(nèi)存溢出(OOM)翻斟。
動(dòng)態(tài)代理
每次都要讓 View 做空判斷,很煩卵佛?
為什么要用動(dòng)態(tài)代理呢杨赤?我們看上面的 get()代碼敞斋,沒次都需要判斷 null 類型截汪,是不是非常麻煩,又因?yàn)檫@里的 View 類型是一個(gè)接口(V extends IBaseView)泛型接口植捎,所以這就好辦了衙解,動(dòng)態(tài)代理完全就可以做到統(tǒng)一的空類型判斷。
使用動(dòng)態(tài)代理之后焰枢,我們?cè)?Presenter 的實(shí)現(xiàn)類中就不需要做 View 層的空類型判斷了蚓峦,這樣既節(jié)省了代碼,雖然沒有多少代碼济锄,但是寫起來還是很煩的暑椰,又讓我們的代碼變得更加優(yōu)雅。