我如何在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()
}
答案 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
中包含的点击事件的回调。我触发回调一次,然后我将对View
和Context
的所有引用都归零,只保留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
的强引用。取消订阅以免费提供此参考。