From 31a55c5ab5a2c077794f4cbd31a26433c7a47875 Mon Sep 17 00:00:00 2001 From: Zac Sweers Date: Sun, 22 Oct 2017 20:11:45 -0700 Subject: [PATCH] Make AndroidLifecycleScopeProvider reusable by deferring backfill to peekLifecycle (#121) * Make AndroidLifecycleScopeProvider reusable by backfilling from peek This moves backfilling out of a one-time thing in the constructor to something we do every time the lifecycle is peeked. It feels a bit weird though, but perhaps necessary because of the fact that arch lifecycle events are emitted _after_ the corresponding callback method. Resolves #114 * Fix imports * Clean up doc --- .../AndroidLifecycleScopeProvider.java | 3 +- .../lifecycle/LifecycleEventsObservable.java | 37 ++++++++++++------- .../uber/autodispose/sample/KotlinActivity.kt | 9 +++-- 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/android/autodispose-android-archcomponents/src/main/java/com/uber/autodispose/android/lifecycle/AndroidLifecycleScopeProvider.java b/android/autodispose-android-archcomponents/src/main/java/com/uber/autodispose/android/lifecycle/AndroidLifecycleScopeProvider.java index 5589ca13e..e8a566f6a 100644 --- a/android/autodispose-android-archcomponents/src/main/java/com/uber/autodispose/android/lifecycle/AndroidLifecycleScopeProvider.java +++ b/android/autodispose-android-archcomponents/src/main/java/com/uber/autodispose/android/lifecycle/AndroidLifecycleScopeProvider.java @@ -49,7 +49,7 @@ public final class AndroidLifecycleScopeProvider case ON_STOP: case ON_DESTROY: default: - throw new LifecycleEndedException(); + throw new LifecycleEndedException("Lifecycle has ended! Last event was " + lastEvent); } } }; @@ -145,6 +145,7 @@ private AndroidLifecycleScopeProvider(Lifecycle lifecycle, } @Override public Lifecycle.Event peekLifecycle() { + lifecycleObservable.backfillEvents(); return lifecycleObservable.getValue(); } diff --git a/android/autodispose-android-archcomponents/src/main/java/com/uber/autodispose/android/lifecycle/LifecycleEventsObservable.java b/android/autodispose-android-archcomponents/src/main/java/com/uber/autodispose/android/lifecycle/LifecycleEventsObservable.java index 224e2c6ed..3efcd4a8d 100644 --- a/android/autodispose-android-archcomponents/src/main/java/com/uber/autodispose/android/lifecycle/LifecycleEventsObservable.java +++ b/android/autodispose-android-archcomponents/src/main/java/com/uber/autodispose/android/lifecycle/LifecycleEventsObservable.java @@ -28,6 +28,10 @@ import io.reactivex.android.MainThreadDisposable; import io.reactivex.subjects.BehaviorSubject; +import static android.arch.lifecycle.Lifecycle.Event.ON_CREATE; +import static android.arch.lifecycle.Lifecycle.Event.ON_DESTROY; +import static android.arch.lifecycle.Lifecycle.Event.ON_RESUME; +import static android.arch.lifecycle.Lifecycle.Event.ON_START; import static android.support.annotation.RestrictTo.Scope.LIBRARY; import static com.uber.autodispose.android.internal.AutoDisposeAndroidUtil.isMainThread; @@ -38,33 +42,38 @@ @SuppressWarnings("CheckReturnValue") LifecycleEventsObservable(Lifecycle lifecycle) { this.lifecycle = lifecycle; - // Backfill if already created for boundary checking - // We do a trick here for corresponding events where we pretend something is created - // upon initialized state so that it assumes the corresponding event is DESTROY. - @Nullable Event correspondingEvent; + } + + Event getValue() { + return eventsObservable.getValue(); + } + + /** + * Backfill if already created for boundary checking. We do a trick here for corresponding events + * where we pretend something is created upon initialized state so that it assumes the + * corresponding event is DESTROY. + */ + void backfillEvents() { + @Nullable Lifecycle.Event correspondingEvent; switch (lifecycle.getCurrentState()) { case INITIALIZED: - correspondingEvent = Event.ON_CREATE; + correspondingEvent = ON_CREATE; break; case CREATED: - correspondingEvent = Event.ON_START; + correspondingEvent = ON_START; break; case STARTED: case RESUMED: - correspondingEvent = Event.ON_RESUME; + correspondingEvent = ON_RESUME; break; case DESTROYED: default: - correspondingEvent = Event.ON_DESTROY; + correspondingEvent = ON_DESTROY; break; } eventsObservable.onNext(correspondingEvent); } - Event getValue() { - return eventsObservable.getValue(); - } - @Override protected void subscribeActual(Observer observer) { if (!isMainThread()) { observer.onError( @@ -96,8 +105,8 @@ static final class ArchLifecycleObserver extends MainThreadDisposable @OnLifecycleEvent(Event.ON_ANY) void onStateChange(LifecycleOwner owner, Event event) { if (!isDisposed()) { - if (!(event == Event.ON_CREATE && eventsObservable.getValue() == event)) { - // Due to the INITIALIZED->ON_CREATE mapping trick we do in the constructor backfill, + if (!(event == ON_CREATE && eventsObservable.getValue() == event)) { + // Due to the INITIALIZED->ON_CREATE mapping trick we do in backfill(), // we fire this conditionally to avoid duplicate CREATE events. eventsObservable.onNext(event); } diff --git a/sample/src/main/kotlin/com/uber/autodispose/sample/KotlinActivity.kt b/sample/src/main/kotlin/com/uber/autodispose/sample/KotlinActivity.kt index f29a31dce..3f587594b 100644 --- a/sample/src/main/kotlin/com/uber/autodispose/sample/KotlinActivity.kt +++ b/sample/src/main/kotlin/com/uber/autodispose/sample/KotlinActivity.kt @@ -32,6 +32,9 @@ import java.util.concurrent.TimeUnit */ class KotlinActivity : AppCompatActivity() { + // Can be reused + private val scopeProvider by lazy { AndroidLifecycleScopeProvider.from(this) } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) Log.d(TAG, "onCreate()") @@ -41,7 +44,7 @@ class KotlinActivity : AppCompatActivity() { // dispose is onDestroy (the opposite of onCreate). Observable.interval(1, TimeUnit.SECONDS) .doOnDispose { Log.i(TAG, "Disposing subscription from onCreate()") } - .autoDisposeWith(AndroidLifecycleScopeProvider.from(this)) + .autoDisposeWith(scopeProvider) .subscribe { num -> Log.i(TAG, "Started in onCreate(), running until onDestroy(): " + num) } } @@ -54,7 +57,7 @@ class KotlinActivity : AppCompatActivity() { // dispose is onStop (the opposite of onStart). Observable.interval(1, TimeUnit.SECONDS) .doOnDispose { Log.i(TAG, "Disposing subscription from onStart()") } - .autoDisposeWith(AndroidLifecycleScopeProvider.from(this)) + .autoDisposeWith(scopeProvider) .subscribe { num -> Log.i(TAG, "Started in onStart(), running until in onStop(): " + num) } } @@ -67,7 +70,7 @@ class KotlinActivity : AppCompatActivity() { // dispose is onPause (the opposite of onResume). Observable.interval(1, TimeUnit.SECONDS) .doOnDispose { Log.i(TAG, "Disposing subscription from onResume()") } - .autoDisposeWith(AndroidLifecycleScopeProvider.from(this)) + .autoDisposeWith(scopeProvider) .subscribe { num -> Log.i(TAG, "Started in onResume(), running until in onPause(): " + num) }