Skip to content

Instantly share code, notes, and snippets.

@bensandee
Last active March 28, 2021 16:55
Show Gist options
  • Save bensandee/58c717aa2a8d9e02d8841671070210c8 to your computer and use it in GitHub Desktop.
Save bensandee/58c717aa2a8d9e02d8841671070210c8 to your computer and use it in GitHub Desktop.
import android.arch.lifecycle.ViewModel
import android.os.Looper
import android.support.annotation.CallSuper
import com.google.errorprone.annotations.CheckReturnValue
import com.uber.autodispose.CompletableSubscribeProxy
import com.uber.autodispose.FlowableSubscribeProxy
import com.uber.autodispose.LifecycleNotStartedException
import com.uber.autodispose.MaybeSubscribeProxy
import com.uber.autodispose.ObservableSubscribeProxy
import com.uber.autodispose.ScopeProvider
import com.uber.autodispose.SingleSubscribeProxy
import io.reactivex.Completable
import io.reactivex.Flowable
import io.reactivex.Maybe
import io.reactivex.MaybeObserver
import io.reactivex.Observable
import io.reactivex.Single
import io.reactivex.android.MainThreadDisposable
import java.util.concurrent.TimeUnit
import com.uber.autodispose.kotlin.autoDisposable
class MyViewModel : BaseViewModel() {
init {
Observable.interval(10, TimeUnit.SECONDS)
.autoDisposable()
.subscribe {
// do something
}
}
}
abstract class BaseViewModel : ViewModel() {
protected val autoDisposeViewModelDelegate by lazy { AutoDisposeViewModelDelegate() }
protected val scopeProvider by lazy { AutoDisposeViewModelScopeProvider(autoDisposeViewModelDelegate) }
@CallSuper
override fun onCleared() {
super.onCleared()
autoDisposeViewModelDelegate.onCleared()
}
// I like to use these extensions to simplify the calls, but they don't really save that much
@CheckReturnValue
protected fun <T> Observable<T>.autoDisposable(): ObservableSubscribeProxy<T> =
autoDisposable(scopeProvider)
@CheckReturnValue
protected fun <T> Flowable<T>.autoDisposable(): FlowableSubscribeProxy<T> =
this.autoDisposable(scopeProvider)
@CheckReturnValue
protected fun <T> Maybe<T>.autoDisposable(): MaybeSubscribeProxy<T> =
this.autoDisposable(scopeProvider)
@CheckReturnValue
protected fun Completable.autoDisposable(): CompletableSubscribeProxy =
this.autoDisposable(scopeProvider)
@CheckReturnValue
protected fun <T> Single<T>.autoDisposable(): SingleSubscribeProxy<T> =
this.autoDisposable(scopeProvider)
}
/** keep all the autodispose stuff in a simple delegate making it easier to compose into existing ViewModel hierarchies */
class AutoDisposeViewModelDelegate {
private val clearListeners: MutableList<OnClearListener> = mutableListOf()
private var cleared = false
fun onCleared() {
cleared = true
clearListeners.forEach {
it.onClear()
}
clearListeners.clear()
}
private interface OnClearListener {
fun onClear()
}
/** adapted/borrowed liberally from viewdetachedmaybe */
internal class ClearedEventMaybe(private val delegate: AutoDisposeViewModelDelegate) : Maybe<Unit>() {
override fun subscribeActual(observer: MaybeObserver<in Unit>) {
val listener = Listener(delegate, observer)
observer.onSubscribe(listener)
// Check we're on the main thread.
if (Looper.myLooper() != Looper.getMainLooper()) {
observer.onError(IllegalStateException("ViewModels can only be bound to on the main thread!"))
return
}
// Check that it's not cleared.
if (delegate.cleared) {
observer.onError(LifecycleNotStartedException("ViewModel is cleared!"))
return
}
if(!listener.isDisposed) {
delegate.clearListeners += listener
}
}
}
internal class Listener(private val delegate: AutoDisposeViewModelDelegate, private val observer: MaybeObserver<in Unit>) :
MainThreadDisposable(),
OnClearListener {
override fun onClear() {
if (!isDisposed) {
observer.onSuccess(Unit)
}
}
override fun onDispose() {
delegate.clearListeners -= this
}
}
}
class AutoDisposeViewModelScopeProvider(private val delegate: AutoDisposeViewModelDelegate) : ScopeProvider {
override fun requestScope(): Maybe<*> {
return AutoDisposeViewModelDelegate.ClearedEventMaybe(delegate)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment