【问题标题】:Should I unsubscribe when using rxbinding?使用 rxbinding 时我应该取消订阅吗?
【发布时间】:2017-01-05 10:02:43
【问题描述】:

这是我如何在 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()
}

【问题讨论】:

    标签: android rx-java kotlin rx-binding


    【解决方案1】:

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

    处理订阅的 RxView.clicks() (或任何来自此的 Observable 库)就像您希望 View 引用本身一样。如果 你在生命周期之外的某个地方传递它(或订阅它) 看,你刚刚泄露了你的整个活动。

    因此,如果您只是在 ViewHolder 中订阅,则无需 取消订阅,就像无需取消注册点击一样 听众你是手动做的吗?

    【讨论】:

      【解决方案2】:

      我做了一个小的测试设置来找出它。它不是 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")
      }
      

      在这个 sn-p 中,我实例化了一个 View,它包含对 Context 的引用。该视图有一个我用Observable 包装的点击事件的回调。我触发了一次回调,然后我将所有对ViewContext 的引用都清空,只保留PhantomReference。然后,在一个单独的线程上,我等到Context 实例被释放。如您所见,我从不退订Observable

      如果你运行代码,它会打印出来

      点击

      收集

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


      这对你意味着什么

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

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

      【讨论】:

      • 感谢您如此详细的解释!在我的情况下,.editorActionEvents() 即使在处理了View 之后仍然会发出事件。我将尝试使用 RxLifecycle 取消订阅 onDestroy
      【解决方案3】:

      是的,你应该unsubscribe when using 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();
      }
      

      【讨论】:

      • 如果 CompositeDisposable 存在但已被释放,请格外小心,当添加另一个 Disposable 时,它​​会立即调用disposable.dispose()。这就是为什么我们应该重新创建 CompositeDisposable 即使 comDisp.isDisposed()
      【解决方案4】:

      是的,如果你查看doc,它会明确表示:

      • 警告: 创建的 observable 保持对 view 的强引用。退订免费此参考。

      【讨论】:

      • 这不能回答问题。如果活动被销毁并且没有任何东西保留对它的引用,那么 observable 也将被收集。问题是,使用 RxBinding 是否会创建任何对仅使用侦听器时不会创建的活动的引用?我的猜测是否定的,除非您使用一些在特殊调度程序上运行的运算符。
      • 如果您查看任何视图构造函数,它都会引用上下文。因此,任何未订阅的绑定都会阻止您的活动被垃圾收集。
      • 如果唯一的参考是循环则不会。这与传递匿名内部类完全相同。
      • 在您取消订阅之前,此 observable 永远不会完成并保留您的参考。
      猜你喜欢
      • 2019-10-25
      • 1970-01-01
      • 1970-01-01
      • 2021-07-22
      • 2012-05-30
      • 1970-01-01
      • 1970-01-01
      • 2018-12-05
      • 1970-01-01
      相关资源
      最近更新 更多