Rxjava常规使用及内存泄漏问题
前言
目前很多项目使用retrofit+Rxjava+mvp的架构开发项目还是比较多的。最近在改一处内存泄漏时候问题时,遇到一个Rxjava相关的泄漏问题。项目中虽然用Rxjava的地方不多,但是项目中用到的东西,还是尽可能搞懂用法、原理。让心里踏实点。
目录
1.本篇主要说下 retrofit+Rxjava的基本用法,及Rxjava内存泄漏相关;
2.主要说下Rxjava实现原理(源码分析);https://www.jianshu.com/p/5d96318b0c03
常规使用
1.添加依赖文件,主要说一点,不要添加那些不必要的依赖;
// 依赖RxAndroid 2X 的依赖库
// 增加RxJava 2X 的依赖库
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
implementation 'io.reactivex.rxjava2:rxjava:2.0.7'
implementation 'com.squareup.retrofit2:retrofit:2.4.0'
implementation "com.squareup.retrofit2:adapter-rxjava2:2.4.0"
implementation "com.squareup.retrofit2:converter-gson:2.4.0"
2.定义retrofit接口
public interface WanAndroidApi {
// 总数据
@GET("project/tree/json")
Observable<ProjectBean> getProject(); // 异步线程 耗时操作
// ITem数据
@GET("project/list/{pageIndex}/json") // ?cid=294
Observable<ProjectItem> getProjectItem(@Path("pageIndex") int pageIndex, @Query("cid") int cid); // 异步线程 耗时操作
}public interface MockApi {
// 总数据
@GET("project/tree/json")
Observable<AllBean> getProject(); // 异步线程 耗时操作
// ITem数据
@GET("project/list/{pageIndex}/json") // ?cid=294
Observable<ItemBean> getProjectItem(@Path("pageIndex") int pageIndex, @Query("cid") int cid); // 异步线程 耗时操作
}
解释下上面代码
定义了两个接口,一个是获取所有数据的,一个是通过页数和cid,获取每条数据,数据是真实数据,WanAndroid 提供的。
3.定义下载工具类
public class HttpUtils {
/**
* 默认 test-a环境
*/
public static String BASE_URL = "https://www.wanandroid.com/";
public static void setBaseUrl(String baseUrl) {
BASE_URL = baseUrl;
}
public static Retrofit getRetrofitInstance(){
// OKHttp客户端
OkHttpClient.Builder httpBuilder = new OkHttpClient.Builder();
// 各种参数配置
OkHttpClient okHttpClient = httpBuilder
// .addNetworkInterceptor(new StethoInterceptor())
.readTimeout(10000, TimeUnit.SECONDS)
.connectTimeout(10000, TimeUnit.SECONDS)
.writeTimeout(10000, TimeUnit.SECONDS)
.build();
return new Retrofit.Builder().baseUrl(BASE_URL)
// TODO 请求用 OKhttp
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
}
}
里面代码也比较简单。
4.调用
private MockApi api;
api = HttpUtils.getRetrofitInstance().create(MockApi.class); //拿到接口对象
// 获取网络API
subscribe = api.getProject() // Observable<ProjectBean>
.subscribeOn(Schedulers.io()) // 上面 异步
.observeOn(AndroidSchedulers.mainThread()) // 下面 主线程
.subscribe(new Consumer<ProjectBean>() {
@Override
public void accept(@NonNull ProjectBean projectBean) throws Exception {
Log.d(TAG, "accept: " + projectBean); // UI 可以做事情
}
});
这样就实现了简单的网络请求,并打印数据
注意点
1.retrofit并不支持网络请求,是对OKhttp的封装;
2.请求到的数据交给Rxjava 处理而已,你如果不用也是没问题的;
Rxjava内存泄漏
1.哪些场景会出现
使用RxJava发布一个订阅后,当页面被finish,此时订阅逻辑还未完成,如果没有及时取消订阅,就会导致Activity/Fragment无法被回收,从而引发内存泄漏.场景还比较多:Activity中执行异步任务、Presenter或ViewModel中执行异步任务、Manager、Adapter相关类中执行异步任务。
如果代码如下:
Observable.create(new ObservableOnSubscribe<XXX>() {
@Override
public void subscribe(final ObservableEmitter<XXX> emitter) throws Exception {
Observable.zip(.....)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<ImpressionBean>() {
@Override
public void accept(XXX impressionBean) throws Exception {
emitter.onNext(impressionBean);
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
emitter.onNext(new XXX());
}
});
}
});
2.如何避免
把整个Observable.create() ...执行subscribe()方法后赋值为Disposable,然后再销毁页面调用下面方法
if (disposable != null && !disposable.isDisposed()) {
disposable.dispose();
}
Presenter或ViewModel中执行异步任务,自定义控件,Manager相关类,自定义Adapter中执行异步任务应该如何防止内存泄露?
在BaseActivity中实现代码:
private CompositeDisposable mCompositeDisposable = new CompositeDisposable();
public void addDisposable(Disposable disposable) {
mCompositeDisposable.add(disposable);
}
@Override
protected void onDestroy() {
super.onDestroy();
mCompositeDisposable.dispose();
}
每次Rxjava异步任务时,把相关的disposable对象传入,onDestroy中统一解绑
在Activity的onDestory()生命周期时,自动解除订阅,以防止因生命周期组件的生命周期而导致的RxJava内存泄漏事件。
其他的解决方式
Rxlife解决内存泄露
autoDisposable解决内存泄露
RxLifecycle解决内存泄露