網(wǎng)絡(luò)請求導(dǎo)致內(nèi)存泄漏
在執(zhí)行網(wǎng)絡(luò)請求的時候,網(wǎng)絡(luò)不穩(wěn)定或者超時的時候,獲取數(shù)據(jù)時間比較長,用戶可能已經(jīng)退出這個界面了,這時候肯定會出現(xiàn)一些問題,首先因?yàn)?code>Presenter還在請求數(shù)據(jù),還持有Activity,就會導(dǎo)致內(nèi)存泄漏
public NotPassPresenter(NotPassActivity activity) {
mActivity = activity;
mRepository = new ExpressRepository();
}
@Override
public void getNotPassRecord() {
mRepository.getNotPassRecord(new IDataCallback<List<NotPassRecord>>() {
@Override
public void success(List<NotPassRecord> notPassRecords) {
getView().showNotPassRecord(notPassRecords);
}
@Override
public void fail(String msg) {
getView().showMsg(msg);
}
});
}
@Override
public NotPassContract.View getView() {
return mActivity;
}
簡單的解決方案
解決方案很簡單,在Presenter中寫一個destroy()方法,里面關(guān)閉資源,釋放掉Activity
public void destory(){
mActivity=null;
}
然后重寫Activity的onDestroy()方法,調(diào)用Presenter.destroy()方法
@Override
protected void onDestroy() {
super.onDestroy();
mNotPassPresenter.destory();
}
這樣Activity關(guān)閉的時候的確不會出現(xiàn)內(nèi)存泄漏,但是獲取到數(shù)據(jù)后getView()方法會返回為空造成內(nèi)存泄漏,所以要判斷不為空;但是說實(shí)話感覺太麻煩了,這些簡單無腦的操作需要重復(fù)寫無數(shù)遍,肯定不能接受的
引入Lifecycles
Android Jetpack有一套可以解決的辦法,使用LiveData和Lifecycles,但是我暫時感覺不是很適應(yīng),所以還是自己來實(shí)現(xiàn)一套。
首先Lifecycles的話感覺還是比較簡單的,從Activity拿到Lifecycle,然后就可以使用它來監(jiān)聽生命周期變化,我們只需要在Presenter里面監(jiān)聽到Destroy的時候就釋放掉資源;activity需要繼承至FragmentActivity,AppCompatActivity也是一樣的,它本身是繼承至FragmentActivity的
public NotPassPresenter(NotPassActivity activity) {
mActivity = activity;
mRepository = new ExpressRepository();
activity.getLifecycle().addObserver(new GenericLifecycleObserver() {
@Override
public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_DESTROY) {
mActivity = null;
mRepository = null;
}
}
});
}
如果在每個Presenter的構(gòu)造方法里面都寫釋放資源的代碼也比較麻煩,可以寫一個BasePresener來統(tǒng)一監(jiān)聽生命周期變化釋放資源;而且通過反射其實(shí)可以釋放里面的所有資源
package com.dhht.baselibrary.app;
import android.arch.lifecycle.GenericLifecycleObserver;
import android.arch.lifecycle.Lifecycle;
import android.arch.lifecycle.LifecycleOwner;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
/**
* @author HanPei
* @date 2019/5/15 下午4:39
*/
public abstract class BasePresenter<T> {
T mView;
Lifecycle mLifecycle;
protected BasePresenter(T view) {
mView = view;
FragmentActivity activity = null;
if (mView instanceof FragmentActivity) {
activity = (FragmentActivity) mView;
} else if (mView instanceof Fragment) {
activity = ((Fragment) mView).getActivity();
}
if (activity != null) {
mLifecycle = activity.getLifecycle();
mLifecycle.addObserver(new GenericLifecycleObserver() {
@Override
public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_DESTROY) {
release();
}
}
});
}
}
/**
* 釋放資源
*/
private void release() {
mView = null;
mLifecycle = null;
/* Observable.just(0)
.observeOn(Schedulers.newThread())
.subscribe(new Consumer<Integer>() {
@Override
public void accept(Integer integer) throws Exception {
//釋放非靜態(tài)變量
Field[] fields = getClass().getDeclaredFields();
for (Field field : fields) {
try {
field.setAccessible(true);
//排除非靜態(tài)變量和基本類型
if (!field.getType().isPrimitive() && !Modifier.isStatic(field.getModifiers())) {
field.set(this, null);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
});*/
}
/**
* 獲取View
*
* @return
*/
protected T getView() {
return mView;
}
protected Lifecycle getLifecycle() {
return mLifecycle;
}
}
取消數(shù)據(jù)請求
上面的操作會自動釋放資源,但是還是需要在數(shù)據(jù)請求完成的時候判斷View是否為空,為了避免判空我們可以取消請求,或者不執(zhí)行數(shù)據(jù)返回回調(diào);RxLifecycle 該項(xiàng)目是為了防止RxJava 中subscription導(dǎo)致內(nèi)存泄漏而誕生的,核心思想是通過監(jiān)聽 Activity、Fragment 的生命周期,來自動斷開 subscription 以防止內(nèi)存泄漏。
基本用法如下:
myObservable
.compose(RxLifecycle.bindUntilEvent(lifecycle, ActivityEvent.DESTROY))
.subscribe();
其實(shí)和上面的道理一樣,當(dāng)監(jiān)聽到Destroy的時候中斷數(shù)據(jù)請求,那么IDataCallback就不會執(zhí)行回調(diào),自然不用去管getView()是否會為空;
方法其實(shí)還可以,只是我每次調(diào)用Model層的時候都需要把當(dāng)前頁面的lifecycle傳進(jìn)去,這個參數(shù)其實(shí)對于Model層來說是沒有意義的,但是我的接口卻必須加上這個參數(shù),感覺還是有些不好;
IDataCallback執(zhí)行回調(diào),那應(yīng)該可以從它入手
public interface IDataCallback<T> {
/**
* 獲取數(shù)據(jù)成功
*
* @param t
*/
void success(T t);
/**
* 獲取數(shù)據(jù)失敗
*
* @param msg
*/
void fail(String msg);
}
這是自己定義的返回數(shù)據(jù)的接口,很簡單,每次獲取到數(shù)據(jù)后調(diào)用回調(diào)方法
mRetrofitApi.getNotPassRecord()
.compose(Retrofite.applySchedulers())
.subscribe(new BaseObserver<List<NotPassRecord>>() {
@Override
public void sucess(List<NotPassRecord> notPassRecords) {
callback.success(notPassRecords);
}
@Override
public void erro(String msg) {
callback.fail(msg);
}
});
我們想在監(jiān)聽到頁面關(guān)閉的時候不執(zhí)行回調(diào),就需要實(shí)現(xiàn)其中的方法,改動一下;
public abstract class BaseDataCallBack<T> implements IDataCallback<T> {
/**
* 是否中斷回調(diào)
*/
private boolean breakOff;
public BaseDataCallBack() {
}
public BaseDataCallBack(Lifecycle lifecycle) {
lifecycle.addObserver(new GenericLifecycleObserver() {
@Override
public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
if (event.equals(Lifecycle.Event.ON_DESTROY)) {
breakOff = true;
}
}
});
}
@Override
public void success(T t) {
if (!breakOff) {
dataBack(t);
}
}
@Override
public void fail(String msg) {
if (!breakOff) {
erroBack(msg);
}
}
/**
* 數(shù)據(jù)返回
*
* @param t
*/
protected abstract void dataBack(T t);
/**
* 返回出錯信息
*
* @param msg
*/
protected abstract void erroBack(String msg);
}
也非常簡單,構(gòu)造方法可以傳入Lifecycle,監(jiān)聽到生命周期結(jié)束,那么不執(zhí)行新的數(shù)據(jù)返回的回調(diào)方法,請求數(shù)據(jù)在設(shè)置監(jiān)聽的時候,重寫這兩個新的抽象方法就好了
public void getNotPassRecord() {
mRepository.getNotPassRecord(new BaseDataCallBack<List<NotPassRecord>>() {
@Override
protected void dataBack(List<NotPassRecord> notPassRecords) {
getView().showNotPassRecord(notPassRecords);
}
@Override
protected void erroBack(String msg) {
getView().showMsg(msg);
}
});
}
感覺也還可以吧,沒有依賴其他的框架,實(shí)現(xiàn)起來很簡單,的確也解決了問題,用起來也方便;和RxLifecycle比起來的話,只是取消了回調(diào),沒取消數(shù)據(jù)請求,問題不大