使用 rxbinding 时我应该取消订阅吗?
Should I unsubscribe when using rxbinding?
这是我在 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)
returnsSubscription
。我应该保留它作为参考并退订 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()
}
是的,如果您查看 doc,它明确表示:
- Warning: The created observable keeps a strong reference to
view
. Unsubscribe to free this reference.
我做了一个小测试设置来找出它。它不是 Android 应用程序,但它模拟了 class 关系。这是它的样子:
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
.
如果你运行代码,它会打印
Click
Collected
然后终止证明对 Context
的引用确实被释放了。
这对您意味着什么
如您所见,如果对它的唯一引用是循环引用,Observable
将不会阻止收集引用的对象。您可以在 this question.
中阅读有关循环引用的更多信息
然而,情况并非总是如此。根据您在可观察链中使用的运算符,引用 can 会被泄露,例如通过调度程序或者如果您将它与无限可观察对象合并,例如 interval()
. Explictly unsubscribing from an observable is always a good idea and you can reduce the necessary boilerplate by using something like RxLifecycle.
是的,你应该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();
}
我认为 Jake Wharton(库的创建者)给出了 best answer:
Treat a subscribed RxView.clicks() (or any Observable from this
library for that matter) like you would the View reference itself. If
you pass it (or subscribe to it) somewhere outside the lifetime of the
View, you've just leaked your entire activity.
So if you're just subscribing inside your ViewHolder there's no need
to unsubscribe just like there'd be no need to unregister a click
listener were you doing it manually.
这是我在 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)
returnsSubscription
。我应该保留它作为参考并退订 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()
}
是的,如果您查看 doc,它明确表示:
- Warning: The created observable keeps a strong reference to
view
. Unsubscribe to free this reference.
我做了一个小测试设置来找出它。它不是 Android 应用程序,但它模拟了 class 关系。这是它的样子:
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
.
如果你运行代码,它会打印
Click
Collected
然后终止证明对 Context
的引用确实被释放了。
这对您意味着什么
如您所见,如果对它的唯一引用是循环引用,Observable
将不会阻止收集引用的对象。您可以在 this question.
然而,情况并非总是如此。根据您在可观察链中使用的运算符,引用 can 会被泄露,例如通过调度程序或者如果您将它与无限可观察对象合并,例如 interval()
. Explictly unsubscribing from an observable is always a good idea and you can reduce the necessary boilerplate by using something like RxLifecycle.
是的,你应该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();
}
我认为 Jake Wharton(库的创建者)给出了 best answer:
Treat a subscribed RxView.clicks() (or any Observable from this library for that matter) like you would the View reference itself. If you pass it (or subscribe to it) somewhere outside the lifetime of the View, you've just leaked your entire activity.
So if you're just subscribing inside your ViewHolder there's no need to unsubscribe just like there'd be no need to unregister a click listener were you doing it manually.