网络请求导致内存泄漏
在执行网络请求的时候,网络不稳定或者超时的时候,获取数据时间比较长,用户可能已经退出这个界面了,这时候肯定会出现一些问题,首先因为Presenter还在请求数据,还持有Activity,就会导致内存泄漏。
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()方法,里面关闭资源,释放掉Activity。
public void destory(){
mActivity=null;
}
然后重写Activity的onDestroy()方法,调用Presenter.destroy()方法。
@Override
protected void onDestroy() {
super.onDestroy();
mNotPassPresenter.destory();
}
这样Activity关闭的时候的确不会出现内存泄漏,但是获取到数据后getView()方法会返回为空造成内存泄漏,所以要判断不为空;但是说实话感觉太麻烦了,这些简单无脑的操作需要重复写无数遍,肯定不能接受的。
引入Lifecycles
Android Jetpack有一套可以解决的办法,使用LiveData和Lifecycles,但是我暂时感觉不是很适应,所以还是自己来实现一套。
首先Lifecycles的话感觉还是比较简单的,从Activity拿到Lifecycle,然后就可以使用它来监听生命周期变化,我们只需要在Presenter里面监听到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的构造方法里面都写释放资源的代码也比较麻烦,可以写一个BasePresener来统一监听生命周期变化释放资源;而且通过反射其实可以释放里面的所有资源。
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 {
//释放非静态变量
Field[] fields = getClass().getDeclaredFields();
for (Field field : fields) {
try {
field.setAccessible(true);
//排除非静态变量和基本类型
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;
}
}
取消数据请求
上面的操作会自动释放资源,但是还是需要在数据请求完成的时候判断View是否为空,为了避免判空我们可以取消请求,或者不执行数据返回回调;RxLifecycle 该项目是为了防止RxJava 中subscription导致内存泄漏而诞生的,核心思想是通过监听 Activity、Fragment 的生命周期,来自动断开 subscription 以防止内存泄漏。
基本用法如下:
myObservable
.compose(RxLifecycle.bindUntilEvent(lifecycle, ActivityEvent.DESTROY))
.subscribe();
其实和上面的道理一样,当监听到Destroy的时候中断数据请求,那么IDataCallback就不会执行回调,自然不用去管getView()是否会为空。
方法其实还可以,只是我每次调用Model层的时候都需要把当前页面的lifecycle传进去,这个参数其实对于Model层来说是没有意义的,但是我的接口却必须加上这个参数,感觉还是有些不好。
IDataCallback执行回调,那应该可以从它入手。
public interface IDataCallback<T> {
/**
* 获取数据成功
*
* @param t
*/
void success(T t);
/**
* 获取数据失败
*
* @param msg
*/
void fail(String msg);
}
这是自己定义的返回数据的接口,很简单,每次获取到数据后调用回调方法。
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);
}
});
我们想在监听到页面关闭的时候不执行回调,就需要实现其中的方法,改动一下。
public abstract class BaseDataCallBack<T> implements IDataCallback<T> {
/**
* 是否中断回调
*/
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);
}
}
/**
* 数据返回
*
* @param t
*/
protected abstract void dataBack(T t);
/**
* 返回出错信息
*
* @param msg
*/
protected abstract void erroBack(String msg);
}
也非常简单,构造方法可以传入Lifecycle,监听到生命周期结束,那么不执行新的数据返回的回调方法,请求数据在设置监听的时候,重写这两个新的抽象方法就好了。
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);
}
});
}
感觉也还可以吧,没有依赖其他的框架,实现起来很简单,的确也解决了问题,用起来也方便;和RxLifecycle比起来的话,只是取消了回调,没取消数据请求,问题不大。