-
概述
基于Android Studio的Profiler和LeakCanary等工具对项目进行内存泄露问题的排查时,发现在使用RxJava结合RxLifecycle进行网络接口请求的时候,在网络请求返回之前关闭Activity后会出现内存泄露,在此记录排查思路。
-
dump hprof
LeakCanary的dump记录如下:
-
排查
代码中使用了匿名类构造HttpObserver:
object : HttpObserver<CaTripDetailDaysResponse, CaTripDetailDaysRequest>( request, RxApiManager.HttpTaskId.TASK_ID_CA_TRIP_DETAIL_DAYS, iViewModelService ) { override fun onSuccess(response: CaTripDetailDaysResponse) { daysData.postValue(response) } override fun onFailure(response: BaseResponse) { super.onFailure(response) daysData.postValue(CaTripDetailDaysResponse().apply { resultCode = response.resultCode resultMsg = response.resultMsg }) } }
constructor(request: K, httpTaskId: RxApiManager.HttpTaskId, mIViewModelService: IViewModelService?) { this.request = request this.mIViewModelService = mIViewModelService this.httpTaskId = httpTaskId }
因为日志中出现了this$0,所以一度以为是匿名内部类的问题,以为是子线程未执行完导致未释放HttpObserver,在HttpObserver的构造方法里把mIViewModelService(在这里的架构里就是Activity本身),从而导致Activity和ViewModel没释放。
但是经过在onError和onComplete等回调方法中把mIViewModelService置为null后,在网路请求迟迟没有完成前(可以通过外网访问内网api来进行模拟)关闭Activity还是会出现泄露,打断点显示,在这个场景下,这些回调方法并没有调用...
再从别的信息分析,从日志上可以看出,有可能是RxApiManager的maps出现的问题,因为RxApiManager.mInstance是静态实例,又因为在HttpObserver的onSubscribe方法中:
override fun onSubscribe(d: Disposable) { disposable = d; if (this.httpTaskId != null) { RxApiManager.get().add(httpTaskId, d) } else if (this.httpTaskStringId != null) { RxApiManager.get().addHttpStringTask(httpTaskStringId, d) } if (showLoading) { mIViewModelService?.preLoading() } }
可以看到,这里会把RxJava网络请求链对象放在RxApiManager的maps中,所以它的maps会一直持有Activity实例,是不是它导致无法释放呢?
但随后我们在HttpObserver中还发现了:
override fun onComplete() { if (this.httpTaskId != null) { RxApiManager.get().cancel(this.httpTaskId) } else if (this.httpTaskStringId != null) { RxApiManager.get().cancelHttpStringTask(this.httpTaskStringId) } if (!manuelFinishLoading && showLoading && finishCount == 1) { mIViewModelService?.finishLoading() } }
可以看到,这里会调用cancel方法进行取消(内部其实是remove掉RxJava网络请求链对象),那为什么还会出现RxApiManager的maps泄露呢?
-
结论
经过场景复现,发现在等待完接口请求正常返回后再关闭Activity的时候不会出现内存泄露,只有在请求尚未完成之前关闭Activity才会出现泄露。再结合RxLifecycle的原理,我们可以做出结论:
RxLifecycle只是将调用链dispose掉,dispose之后调用传递会被终止,因此这就是onError和onComplete未调用的原因,所以在onSubscribe中add到RxApiManager的maps中的Disposable就得不到对应的remove,又因为Disposable调用链中持有了HttpObserver,HttpObserver又持有了Activity和ViewModel(this$0),所以就发生了泄露,解决办法就是在Activity或者ViewModel销毁的时候调用RxApiManager的cancel方法移除对应的调用链。
网友评论