我应该在使用rxbinding时取消订阅吗?

时间:2017-01-05 10:02:43

标签: android rx-java kotlin rx-binding

我如何在Kotlin中使用RxBinding:

override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    reset_password_text_view.clicks().subscribe { presenter.showConfirmSecretQuestionBeforeResetPassword() }
    password_edit_text.textChanges().skip(1).subscribe { presenter.onPasswordChanged(it.toString()) }
    password_edit_text.editorActionEvents().subscribe { presenter.done(password_edit_text.text.toString()) }
}

Observable.subscribe(action)返回Subscription。我应该将其作为参考并取消订阅onPause()onDestroy()吗?

像这样:

private lateinit var resetPasswordClicksSubs: Subscription

override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    resetPasswordClicksSubs = reset_password_text_view.clicks().subscribe { presenter.showConfirmSecretQuestionBeforeResetPassword() }
}

override fun onDestroy() {
    super.onDestroy()
    resetPasswordClicksSubs.unsubscribe()
}

4 个答案:

答案 0 :(得分:9)

我做了一个小测试设置来找出它。它不是Android应用程序,但它模拟了类关系。这是它的样子:

class Context
class View(val context: Context) {
    lateinit var listener: () -> Unit
    fun onClick() = listener.invoke()
}

fun View.clicks() = Observable.fromEmitter<String>({ emitter ->
    listener = { emitter.onNext("Click") }
}, Emitter.BackpressureMode.DROP)


var ref: PhantomReference<Context>? = null

fun main(args: Array<String>) {
    var c: Context? = Context()
    var view: View? = View(c!!)

    view!!.clicks().subscribe(::println)
    view.onClick()
    view = null

    val queue = ReferenceQueue<Context>()
    ref = PhantomReference(c, queue)
    c = null

    val t = thread {
        while (queue.remove(1000) == null) System.gc()
    }
    t.join()

    println("Collected")
}

在此代码段中,我实例化了View,其中包含对Context的引用。该视图包含我在Observable中包含的点击事件的回调。我触发回调一次,然后我将对ViewContext的所有引用都归零,只保留PhantomReference。然后,在一个单独的线程上,我等到Context实例被释放。如您所见,我永远不会取消订阅Observable

如果您运行代码,它将打印

  

点击

     

累计

然后终止证明对Context的引用确实已被释放。

这对您意味着什么

正如您所看到的,Observable不会阻止被引用的对象被收集,如果它对它的唯一引用是循环的。您可以在this question中阅读有关循环引用的更多信息。

但情况并非总是如此。根据您在可观察链中使用的运算符,引用可以泄露,例如由调度程序或如果将其与无限可观察对象合并,如interval()。从一个可观察者中明确取消订阅总是一个好主意,你可以通过RxLifecycle之类的东西来减少必要的样板。

答案 1 :(得分:9)

我认为Jake Wharton(图书馆的创建者)给了best answer

  

对待订阅的RxView.clicks()(或任何Observable)   这个问题的库)就像你对View引用本身一样。如果   你在它的生命周期之外的某个地方传递它(或订阅它)   查看,您刚刚泄露了整个活动。

     

因此,如果您只是在ViewHolder中订阅,那么就没有必要了   取消订阅就像那里一样,不需要取消注册点击   听众你是手动做的。

答案 2 :(得分:3)

是的,您应该unsubscribe when using RxBinding

这是一种方式......(在java中,可以为kotlin调整?)

收集

在您的活动或片段中,将一次性物品添加到您将在onDestroy()处置的CompositeDisposable。

CompositeDisposable mCompD; // collector

Disposable d = RxView.clicks(mButton).subscribe(new Consumer...);

addToDisposables(mCompD, d); // add to collector

public static void addToDisposables(CompositeDisposable compDisp, Disposable d) {
    if (compDisp == null) {
        compDisp = new CompositeDisposable();
    }

    compDisp.add(d);
}

处置

@Override
protected void onDestroy() {
    mCompD.dispose();
    super.onDestroy();
}

答案 3 :(得分:2)

是的,如果你查看doc,它会明确地说:

  
      
  • 警告:创建的observable保留对view的强引用。取消订阅以免费提供此参考。
  •