Rxjava是个异步库,其链式的api调用使用起来非常简洁,优雅,但是不做处理的话很容易出现内存泄露
内存泄露例子:
有个MainActivity,代码如下:
class MainActivity : AppCompatActivity(), View.OnClickListener {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
btn_start_second.setOnClickListener(this)
}
override fun onClick(v: View) {
when (v.id) {
R.id.btn_start_second->{startActivity(Intent(this,SecondActivity::class.java))}
}
}
}
MainActivity有个按钮,打开SecondActivity,SecondActivity代码如下:
class SecondActivity : AppCompatActivity(), View.OnClickListener {
val TAG = this.javaClass.simpleName
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_second)
ds = CompositeDisposable()
btn_start_count.setOnClickListener(this)
}
override fun onClick(v: View) {
when (v.id) {
R.id.btn_start_count -> {
Observable.interval(1, TimeUnit.SECONDS)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
tv_count.text = it.toString()
loge(TAG, "count:$it")
}
}
}
}
override fun onDestroy() {
super.onDestroy()
}
}
SecondActivity中有个Button,TextView,点击Button,TextView不断输出
观察测试:
运行App,开启SecondActivity,点击Button改变TextView的值,然后关闭SecondActivity,如此循环操作多次,发现即时关闭了SecondActivity,TextView依旧在输出:
01-15 17:29:33.563 26906-26906/com.rain.rxjava2demo E/SecondActivity: count:146
01-15 17:29:33.944 26906-26906/com.rain.rxjava2demo E/SecondActivity: count:144
01-15 17:29:34.471 26906-26906/com.rain.rxjava2demo E/SecondActivity: count:150
01-15 17:29:34.563 26906-26906/com.rain.rxjava2demo E/SecondActivity: count:147
01-15 17:29:34.948 26906-26906/com.rain.rxjava2demo E/SecondActivity: count:145
打开Profile分析器,观察Memory情况:
操作几次后,手动调用GC后的内存占用,如下图:
调取当前java堆的快照,发现有多个SecondActivity的引用及SecondActivity#onClick中的泄露
这时SecondActivity的代码做如下更改(只贴关键部分):
private var d: Disposable? = null
private lateinit var ds: CompositeDisposable
override fun onClick(v: View) {
when (v.id) {
R.id.btn_start_count -> {
d = Observable.interval(1, TimeUnit.SECONDS)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
tv_count.text = it.toString()
loge(TAG, "count:$it")
}
ds.add(d!!)
}
}
}
override fun onDestroy() {
super.onDestroy()
// 这样处理发现依然存在内存泄露
ds.clear()
}
在onDestroy中,ds.clear(),即所有的disposable调用d.dispose() 方法,这样就不会内存泄露了吗?
验证测试:
依旧打开SecondActivity,并点击按钮,这样操作3次,先看log:
01-15 17:49:02.292 7197-7197/com.rain.rxjava2demo E/SecondActivity: count:0
01-15 17:49:05.384 7197-7197/com.rain.rxjava2demo E/SecondActivity: count:0
01-15 17:49:07.482 7197-7197/com.rain.rxjava2demo E/SecondActivity: count:0
发现subscribe中的代码确实不执行了,
看内存堆:
内存占用在65M左右,但是发现依旧存在SecondActivity的引用,先别急,过一段时间再看一次内存占用情况,会发现SecondActivity的引用没有了,虽然手动强制了GC,但是并不一定会立即进行回收,这点要注意。
有文章说d.dispose()只是切除了上下游的数据传递,并没有切断上下游的引用关系,代码做如下修改:
d = Observable.interval(1, TimeUnit.SECONDS)
.onTerminateDetach()// 当执行了d.dispose()方法后将解除上下游的引用
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
tv_count.text = it.toString()
loge(TAG, "count:$it")
}
ds.add(d!!)
代码修改的部分,调用 onTerminateDetach()操作符,看下源码的注释:
Nulls out references to the upstream producer and downstream Observer if the sequence is terminated or downstream calls dispose().
如果序列终止或者下游调用dispose(),那么将解除上下游的引用
有文章说onTerminateDetach操作符要和subscription.unsubscribe() 结合使用,因为不执行subscription.unsubscribe()的话,onTerminateDetach就不会被触发。这时如果我们对当前的stream再进一步操作,比如使用map操作符,那么需要再次调用onTerminateDetach。
但是我并没有发现二者在内存上的表现有什么不同
结论:
1. 在onDestroy方法中调用dispose()方法
第三方库的使用
1. 使用Rxlifecycle库,缺点是Activity、Fragment要继承RxActivity、RxFragment
简单使用方法如下:
引入库:
implementation "com.trello.rxlifecycle3:rxlifecycle:$rxlifecycle"
implementation "com.trello.rxlifecycle3:rxlifecycle-components:$rxlifecycle"
Activity、Fragment继承RxActivity、RxFragment等,调用时候绑定对应的生命周期,详细用法请查阅官方文档
Observable.interval(1, TimeUnit.SECONDS)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.compose(this.bindUntilEvent(ActivityEvent.DESTROY))
.subscribe {
tv_count.text = it.toString()
loge(TAG, "count:$it")
}
2. 使用autodispose库,
引入:
implementation "com.uber.autodispose:autodispose-android-archcomponents-ktx:$autodispose_version"
implementation "com.uber.autodispose:autodispose-lifecycle-ktx:$autodispose_version"
调用如下:
Observable.interval(1, TimeUnit.SECONDS)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.autoDisposable(AndroidLifecycleScopeProvider.from(this,Lifecycle.Event.ON_DESTROY))
.subscribe {
tv_count.text = it.toString()
loge(TAG, "count:$it")
}
这里都是演示最基本的用法
参考文章: