There is how I use RxBinding with 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)
returns Subscription
. Should I keep it as reference and unsubscribe onPause()
or onDestroy()
?
Like this:
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()
}
I've made a small test setup to find it out. It's not an Android app but it simulates the class relationships. Here's what it looks like:
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")
}
In this snippet I instantiate a View
that holds a reference to a Context
. the view has a callback for click events that I wrap in an Observable
. I trigger the callback once, then I null out all references to the View
and the Context
and only keep a PhantomReference
. Then, on a separate thread I wait until the Context
instance is released. As you can see, I'm never unsubscribing from the Observable
.
If you run the code, it will print
Click
Collected
and then terminate proving that the reference to the Context
was indeed released.
What this means for you
As you can see, an Observable
will not prevent referenced objects from being collected if the only references it has to it are circular. You can read more about circular references in this question.
However this isn't always the case. Depending on the operators that you use in the observable chain, the reference can get leaked, e.g. by a scheduler or if you merge it with an infinite observable, like interval()
. Explictly unsubscribing from an observable is always a good idea and you can reduce the necessary boilerplate by using something like RxLifecycle.
I think that Jake Wharton (the creator of the library) gave the 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.
Yes, you should unsubscribe when using RxBinding.
Here's one way... (in java, could be tweaked for kotlin?)
Collect
Within your Activity or Fragment, add disposables to a CompositeDisposable that you'll dispose at onDestroy().
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);
}
Dispose
@Override
protected void onDestroy() {
mCompD.dispose();
super.onDestroy();
}
Yep, if you look in the doc, it explicitely says:
- Warning: The created observable keeps a strong reference to
view
. Unsubscribe to free this reference.