13

下面是我如何将 RxBinding 与 Kotlin 一起使用:

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

4 回答 4

15

我认为 Jake Wharton(图书馆的创建者)给出了最好的答案

像对待 View 引用本身一样对待订阅的 RxView.clicks() (或者这个库中的任何 Observable )。如果您在视图生命周期之外的某个地方传递(或订阅)它,那么您只是泄露了您的整个活动。

因此,如果您只是在 ViewHolder 中订阅,则无需取消订阅,就像您手动操作时无需取消注册点击监听器一样。

于 2017-07-03T12:39:34.783 回答
13

我做了一个小的测试设置来找出它。它不是 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包含对 a 的引用的 a Context。该视图有一个针对我包装在Observable. 我触发了一次回调,然后我清空所有对 theView和 the 的引用,Context并且只保留一个PhantomReference. 然后,在一个单独的线程上,我等到Context实例被释放。如您所见,我从不退订Observable.

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

点击

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


这对你意味着什么

如您所见,Observable如果它对它的唯一引用是循环的,则不会阻止收集引用的对象。您可以在此问题中阅读有关循环引用的更多信息。

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

于 2017-01-05T15:23:46.190 回答
5

是的,您应该在使用 RxBinding 时取消订阅

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

搜集

在您的 Activity 或 Fragment 中,将一次性用品添加到您将在 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();
}
于 2017-05-06T18:38:31.593 回答
4

是的,如果您查看文档,它会明确表示:

  • 警告:创建的 observable 对view. 退订免费此参考。
于 2017-01-05T10:17:56.370 回答