有关Android Lifecycle的一些事

这是一篇关于AutoDispose/RxLifecycle/Google Lifecycle的详细分析,以做记录。

Architecture Components Lifecycle

Google之前推出过一套Architecture Components,里面的Lifecycle组件是LiveData等的基础组件。

image.png

具体的生命周期过程不再介绍。我们来看google官网对整个生命周期流动的解释。

在该Lifecycle组件中,与传统的生命周期不同,只定义了五种状态,分别是:

  • INITIALIZED 最初始的状态
  • DESTROYED
  • CREATED
  • STARTED
  • RESUMED

上图中向右的箭头很好理解,每过来一个events会发生生命周期状态的变更
向左的箭头可以看成状态的回滚, 如果在RESUMED状态发生了onPause()事件,则状态回滚到RESUMED之前的STARTED状态

image.png

这幅图是我看完源码后对整个架构的总结。整个组件由三大部分组成:LifecycleRegistry & LifecycleOwner & LifecycleObserver

LifecycleOwner

生命周期事件分发者。例如我们最熟悉的Activity/Fragment。它们在生命周期发生变化时发出相应的EventLifecycleRegistry

LifecycleObserver

生命周期监听者。通过注解将处理函数与希望监听的Event绑定,当相应的Event发生时,LifecycleRegistry会通知相应的函数进行处理。

LifecycleRegistry

控制中心。它负责控制state的转换、接受分发event事件。其实个人觉得Lifecycle组件与EventBus很类似? 但以下代码表现了他们的不同:

while (mObserverCurrentState != mState) {
    Event event = mObserverCurrentState.isAtLeast(mState)
            ? downEvent(mObserverCurrentState) : upEvent(mObserverCurrentState);
    mObserverCurrentState = getStateAfter(event);
    mCallback.onStateChanged(mLifecycleOwner, event);
}

假设现在的状态为RESUMED,但是observer的状态还在CREATED,那么就会走upEvent(mObserverCurrentState)依次向observer发送start&resume事件。

假设现在的状态为CREATED,但是observer的状态还在RESUMED,说明observer状态超前了,那么就会走downEvent(mObserverCurrentState)依次向observer发送pause&stop事件。

侵入性问题

Lifecycle组件为了降低侵入性,首先通过registerActivityLifecycleCallbacks给每个启动的Activity创建了一个ReportFragment

@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
    if (activity instanceof FragmentActivity) {
        ((FragmentActivity) activity).getSupportFragmentManager()
                .registerFragmentLifecycleCallbacks(mFragmentCallback, true);
    }
    // ProcessLifecycleOwner should always correctly work and some activities may not extend
    // FragmentActivity from support lib, so we use framework fragments for activities
    android.app.FragmentManager manager = activity.getFragmentManager();
    if (manager.findFragmentByTag(REPORT_FRAGMENT_TAG) == null) {
        manager.beginTransaction().add(new ReportFragment(), REPORT_FRAGMENT_TAG).commit();
        // Hopefully, we are the first to make a transaction.
        manager.executePendingTransactions();
    }
}

然后在ReportFragment中真正根据生命周期去触发Activity的Lifeycle事件。当然我并不明白它为什么不直接用ActivityLifecycleCallback非要通过Fragment中转。有知道的朋友解释一下~

RxLifecycle

因为Rxjava2的流行,顺其自然的出现了RxLifecycle组件,主要防止Rxjava的泄漏,在生命周期结束时取消Subscription

image.png

为了依旧实现Rxjava的链式调用,RxLifecycle很巧妙的使用了TakeUtil运算符

TakeUtil

image.png

看图非常容易理解,不再解释。

LifecycleProvider

与前面类似,Activity等作为生命周期的提供者LifecycleProvider,该Provider会提供一个转换器LifecycleTransformer

image.png
@Override
public ObservableSource<T> apply(Observable<T> upstream) {
    //当observable发射时停止upstream
    return upstream.takeUntil(observable);
}

即经过该转换器的转化,上游事件发生者会被另一个Observable限制发射。那么这个作为限制的Observable究竟是什么呢:

observable1

过滤出指定事件,这个可以实现到指定的Event时取消事件绑定。

private static <R> Observable<R> takeUntilEvent(final Observable<R> lifecycle, final R event) {
    return lifecycle.filter(new Predicate<R>() {
        @Override
        public boolean test(R lifecycleEvent) throws Exception {
            return lifecycleEvent.equals(event);
        }
    });
}

observable2

//R ActionEvent
private static <R> Observable<Boolean> takeUntilCorrespondingEvent(final Observable<R> lifecycle,
                                                                   final Function<R, R> correspondingEvents) {
    return Observable.combineLatest(
        //拿一个
        lifecycle.take(1).map(correspondingEvents),
        lifecycle.skip(1),
        new BiFunction<R, R, Boolean>() {
            @Override
            public Boolean apply(R bindUntilEvent, R lifecycleEvent) throws Exception {
                return lifecycleEvent.equals(bindUntilEvent);
            }
        })
        .onErrorReturn(Functions.RESUME_FUNCTION)
        .filter(Functions.SHOULD_COMPLETE);
}

这个略微复杂,举例来说,如果你在onCreatebindLifecycle,那Rxlifecycle会自动帮你在onDestroy里注销。onStart&onStop同理。所以代码就比较好理解,lifecycle.take(1).map(correspondingEvents)会找到对应的endEvent,然后与后续发射的Event对比,如果两者相等说明已经到了该结束的时刻了!

BehaviorSubject

BehaviorSubject是观察Lifecycle事件,在onCreate的时候它发射create event。我们把它跟真实的Observable绑定在一起,实现前面的takeUtil。当BehaviorSubject发射特定的事件时,真实的Observable退出。

  // observer will receive the "one", "two" and "three" events, but not "zero"
  BehaviorSubject<Object> subject = BehaviorSubject.create();
  subject.onNext("zero");
  subject.onNext("one");
  subject.subscribe(observer);
  subject.onNext("two");
  subject.onNext("three");

可以看到,上述中observer只会收到one,two,three事件。而在RxActivity中,

@Override
@CallSuper
protected void onStart() {
    super.onStart();
    lifecycleSubject.onNext(ActivityEvent.START);
}

所以如果在onStart中绑定lifecycle事件,BehaviorSubject会收到从onStart开始的事件,这是不是跟前面的observable2结合起来看就一目了然了。

侵入性

Rxlifecycle具有很强的侵入性。从sample中也可以看到,你需要继承RxActivity,并且悲剧的是从前面截图可以看到,整篇代码在ide中飘黄,大概因为Rxjava2强制subscribe后返回的结果需要@CheckReturnValue。不过这也不能怪Rxlifecycle..毕竟取消了它的代码也还是会有警告- -

有关MVP

其实我们可以自己改造Rxlifecycle,我们知道,Presenter也是有生命周期的。最简单而言attachView() & detachView()。因此我们可以做如下改造:

//核心代码
@Override
@CallSuper
public void attachView(V view) {
    super.attachView(view);
    lifecycleSubject.onNext(PresenterEvent.ATTACH);
}

Autodispose

Uber的AutodisposeRxjava2作用类似。但他很巧妙的解决了上面的问题。

image.png

当然,他的整体设计也更复杂一些。

ObservableSubscirbeProxy(single..类同)

它解决了Rxjava2中警告的问题。这里的subscribe不再是Observable.subscribe,而是在外面包了一层Subscribeproxy。简单来说,对于autoDispose而言,他其实打破了整个Rxjava subscribe to Observer的链路,而是完全由自己监管。

image.png

可以看到,将一个上游事件发射者转变为subscribeProxy

所以最终subscribe(observer)时,实际发生的是

AutoDisposeObservable<>(observableSource, scope()).subscribe(observer)

这里的observableSource是上游事件,scopeMaybe<?>。我们重点关注scope是什么:


autoDisposable(deferredResolvedLifecycle(checkNotNull(provider, "provider == null")));


AndroidLifecycleScopeProvider.from(this)

LifecycleEventsObservable

先插一句介绍一下这玩意。

接受Lifecycle作为参数一般是Activity...。它的内部有一个ArchLifecycleObserver

image.png

我们可以把它看成一个发射LifecycleEvent的事件源。该事件源包含真实的Lifecycle,当它subscirbe(observer)时,会Lifecycle.add将它与lifecycle绑定起来。当observer监听到事件时,会调用actualObserver.onNext
这边不理解可以先忽略,后面会再回过头来看。

LifecycleScopeProvider

生命周期的提供者。包含上述的LifecycleEventsObservable

关键代码

autoDisposable(deferredResolvedLifecycle(checkNotNull(provider, "provider == null")));

这里的deferredResolvedLifecycle返回了前面的Maybe<?>:

return Maybe.defer(new Callable<MaybeSource<? extends LifecycleEndNotification>>() {
      @Override public MaybeSource<? extends LifecycleEndNotification> call() throws Exception {
        E lastEvent = provider.peekLifecycle();
        //outsideLifecycle
        if (checkStartBoundary && lastEvent == null) {
          LifecycleNotStartedException exception = new LifecycleNotStartedException();
          Consumer<? super OutsideLifecycleException> handler
              = AutoDisposePlugins.getOutsideLifecycleHandler();
          if (handler != null) {
            handler.accept(exception);
            return Maybe.just(LifecycleEndNotification.INSTANCE);
          } else {
            throw exception;
          }
        }
        E endEvent;
        try {
          endEvent = provider.correspondingEvents()
              .apply(lastEvent);
        } catch (Exception e) {
          if (checkEndBoundary && e instanceof LifecycleEndedException) {
            Consumer<? super OutsideLifecycleException> handler
                = AutoDisposePlugins.getOutsideLifecycleHandler();
            if (handler != null) {
              handler.accept((LifecycleEndedException) e);
              return Maybe.just(LifecycleEndNotification.INSTANCE);
            } else {
              throw e;
            }
          } else {
            return Maybe.error(e);
          }
        }
        return resolveScopeFromLifecycle(provider.lifecycle(), endEvent);
      }
    });

不懂这里为什么要Maybe.defer

这里的lastEvent指的是上一个事件,endEvent指的是上一个事件对应的结束事件。。比如Create对应Destroy

  /**
   * @param lifecycle the stream of lifecycle events
   * @param endEvent the target end event
   * @param <E> the lifecycle event type
   * @return a resolved {@link Maybe} representation of a given lifecycle, targeting the given event
   */
  public static <E> Maybe<LifecycleEndNotification> resolveScopeFromLifecycle(
      Observable<E> lifecycle,
      final E endEvent) {
    return lifecycle.skip(1)
        .map(new Function<E, Boolean>() {
          @Override public Boolean apply(E e) throws Exception {
            return e.equals(endEvent);
          }
        })
        .filter(IDENTITY_BOOLEAN_PREDICATE)
        .map(TRANSFORM_TO_END)
        .firstElement();
  }

这段代码是不是在Rxlifecycle中似曾相识。。。废话,他也是得到与endEvent匹配的事件。这里看到provider.lifecycle()了吗,我们可以回到上面的LifecycleEventsObservable。当它被订阅的时候,就真正与Activity这种LifecycleOwner绑定了起来,当observer监听到生命周期事件并发生onStateChange时,会调用actualObserver.onNext

扯了一大堆,结论就是scope返回了一个Observable,它只会在发生对应的endEvent时发出事件。

不得不感慨uber工程师设计的精妙。。

那我们再回过头来看最初的代码:

AutoDisposeObservable<>(observableSource, scope()).subscribe(observer)

-->

source.subscribe(new AutoDisposingObserverImpl<>(scope, observer));

AutoDisposingObserverImpl是一个很 有趣的类,它让scope被一个DisposableMaybeObserver订阅,当scope发出endEvent事件时,该observer会把mainDisposable的状态设置为DISPOSED,而当真实的observer收到事件时,如果发现mainDisposable已经为DISPOSED就不会处理接受到的真实事件。

我把整个过程过程了一张图,作为总结。

从上图可以看到,LifecycleScopeProvider包括LifecycleEventsObservable,后者与endEvent组合生成了一个叫scope()的事件发生器。当这个java source.subscribe(new AutoDisposingObserverImpl<>(scope, observer));发生时,scopeDisposableMaybeObserver订阅。

这时如果Lifecycle发出LifecycleEvent,因为ArchLifecycleObserver是一个LifecycleObserver,他可以监听到生命周期事件。并且传递给下流downStream,经过之前的串联运算符的过滤后到DisposableMaybeObserver就是真正的endEvent。可以看到顺理成章完成了我们希望达到的目的。

Why Not RxLifecycle?

Why Not RxLifecycle

挺有趣的,Rxlifecycle原作者写了一篇为什么不要使用rxlifecycle - -

他提出了以下几点原因并给出了自己的优化建议,当然有些个人觉得是他强迫症了- -

  • 他认为自动化的生命周期检测有的时候会让人感到困扰,有的时候你的组件可能不在activity里面,那你需要把lifecycle传递给他,并且寄希望于在正确的生命周期时间进行unsubscribe(这个正确的可能很难做到).比如你有一个adapter,它的数据来源于observable.关键在于你无法知道什么时候可以unsubscribe..因为你在adapter里没有地方给你生命周期函数去subscribe....当然你可以使用bindUntilEvent(),但是就削弱了rxlifecycle提供的功能
  • 经常其实我们还是会手动去处理subscription。比如adapter这个例子,可能我现在正在监听一个数据源,但是之后想监听另一个,那就需要把上一个取消掉。这个时候用户可能会感到很奇怪,为什么我们在一个地方手动ubsubscribe()同时又在另一个地方用了rxlifecycle
  • 还有前面提到过的问题,作者也进行了罗列。

作者还顺便略推荐了一把autodispose...至于他说的fewer restrictions on when it can be used in a stream我没怎么看懂。个人觉得可能想表达的是,Rxlifecycle使用了takeUtil运算符,直接就侵入了原有的流。而autoDispose还是完整的保留了原有的流。有知道的麻烦回复一下~

其实Rxlifecycle之前有较多问题是因为基于rxjava1,后来他迁到2.x版本后还是解决了很多问题的,比如也开始支持Single等等。

最后作者给出了一些优化建议与模式,我们开发可以参考:

  • 手动处理subscription。其实我们可以看到一种做法,把subscriptions存储在compositeSubscription(一个容器)。在合适的时候只要把compositeSubscription取消即可把全部订阅都取消。
  • 对于类似adapter的组件,他应该把自己的订阅抛到上层。例如adapter,就应该把subscription返回给activity让他去进行生命周期的管理。
  • kotlin天然的null优势,让开发者通过一行代码mySubscription?.unsubscribe()即可取消订阅

上面三种模式,最终希望达到同一个目的。但我们在使用的时候还是要注重业务场景,例如后两者明显是为Rxjava量身打造的,而前一种很适合用于单组件,例如前面提到的adapter,它就不会遇到后两者拥有的问题。

本文完结。有些问题还等待解决。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容