Android Jetpack 之 Lifecycle - 4

618 阅读7分钟

这是我参与更文挑战的第19天,活动详情查看: 更文挑战

源码分析

生命周期注册者 (LifecycleRegistry)

addObserver

@Override
    public void addObserver(@NonNull LifecycleObserver observer) {
        //todo  这段注释要再理解下
        // 上文在dispatchEvent前后进行的pushParentState和popParentState就是为了解决这个问题:
        // 如果在onStart中注销了observer又重新注册,这时重新注册的observer初始的状态为INITIALIZED;
        // 而如果想执行ON_START的对应回调,需要newObserver处于CREATED状态(之前的状态因为removeObserver,不存在于mObserverMap中)
       // mParentStates的作用就是为newObserver提供了CREATED这个状态

    
        // 如果不是 DESTROYED,则从 INITIALIZED 开始分发
        State initialState = mState == DESTROYED ? DESTROYED : INITIALIZED;
        // ObserverWithState 用于分发事件给 observer
        // ObserverWithState 里会做 Observer 的转换
        ObserverWithState statefulObserver = new ObserverWithState(observer, initialState);
        // 放入 map 中
        ObserverWithState previous = mObserverMap.putIfAbsent(observer, statefulObserver);

        if (previous != null) {
            return;
        }
        LifecycleOwner lifecycleOwner = mLifecycleOwner.get();
        if (lifecycleOwner == null) {
            // it is null we should be destroyed. Fallback quickly
            return;
        }

        // isReentrance 表示是否在分发事件时新添加了 observer
    	// 举个例子:在 observer 在 onStart() 中又调用了 addObserver()
        boolean isReentrance = mAddingObserverCounter != 0 || mHandlingEvent;
        // 引入 parentState, targetState 等都是为了保证列表中后加的 Observer 的状态不能大于前面的, 这样做之后,如果列表第一个和最后一个的状态和 LifecycleRegistry.mState 相等时,就说明状态同步完成了。
        // 计算需要分发的状态
        State targetState = calculateTargetState(observer);
        mAddingObserverCounter++;
        // 将事件逐步分发到 targetState
        while ((statefulObserver.mState.compareTo(targetState) < 0
                && mObserverMap.contains(observer))) {
            // 如果 statefulObserver.state 小于 targetState
            pushParentState(statefulObserver.mState);
            // 如果 state 为 STARTED,则 upEvent(state) 则为 ON_RESUME
            statefulObserver.dispatchEvent(lifecycleOwner, upEvent(statefulObserver.mState));
            popParentState();
            // mState / subling may have been changed recalculate
            targetState = calculateTargetState(observer);
        }

        //addObserver()主要考虑了 Reentrance 的情况,即在observer的事件分发中,又添加了新的 observer的情况。
        if (!isReentrance) {
            // we do sync only on the top level.
             // 当前为重入,则不进行同步
            sync();
        }
        mAddingObserverCounter--;
    }
private State calculateTargetState(LifecycleObserver observer) {
     	// 获取上一个添加的 observer	
        Entry<LifecycleObserver, ObserverWithState> previous = mObserverMap.ceil(observer);

        State siblingState = previous != null ? previous.getValue().mState : null;
    	// mParentStates 是个 List,它的添加和删除分别由 pushParentState() 和 popParentState(),它们是成对出现的,在 dispatchEvent 的前后
    	// 在这种 case 下,会存在 parentState:在 dispatchEvent 时,又调用了 addObserver(),即上面说的 isReentrance
        State parentState = !mParentStates.isEmpty() ? mParentStates.get(mParentStates.size() - 1)
                : null;
    	// 这里的计算是获取更合适的状态
    	// 考虑以下这种 case:某个 observer 在 onStart() 中再调用 addObserver,那这个 observer 理应使用 STARTED 状态分发,而当前状态即 mState 可能是 RESUMED,再在 sync() 中进行同步
        return min(min(mState, siblingState), parentState);
    }

addObserver 并不是简单的将 Observer 加入 map 中, 首先它会做上文提到的 Observer 转换。 其次需要考虑“重入问题”。 所谓的“重入问题”,就是 addObserver 会触发 Observer 生命周期函数的调用,而 Observer 在生命周期函数中又调用了 addObserver 等方法。 因此, LifecycleRegistry 用变量 mAddingObserverCounter 和 mHandlingEvent 来判断是否处于重入状态。

其他 LifecycleOnwer

LifecycleService

ServiceLifecycleDispatcher 将事件派发重新推到主线程消息队列,用于保证确保回调在 Service 生命周期回调后再调用。

ProcessLifecycleOwner

用于监听整个应用的前后台切换。也是利用 ActivityLifecycleCallback 监听每个 Activity 的生命周期,如果 onStop 事件后,没有监听到任意的 onStart 事件,那么 ProcessLifecycleOwner 就会认为整个应用切换到后台,同时留下一个标志。如果监听到 onStart 事件,同时检查有标志那么就会认为应用回到前台。

juejin.cn/post/5d15bb…

linxiaotao.github.io/2019/01/14/…]

【奇技淫巧】使用 ProcessLifecycle 优雅地监听应用前后台切换 todo

其他

todo 下面这段可以放到其他文章去

能感知生命周期的组件的最佳实践

  • 尽量让您的 UI 控制器(activity 和 fragment)别什么都自己扛着。它们不应试图获取自身的数据,而是应该通过 ViewModel,并观察一个 LiveData 对象来在视图上针对变动做出反应。
  • 尝试编写数据驱动的 UI,并让您的 UI 控制器负责在数据变化的时候更新视图、或者把用户的行为通知给 ViewModel。
  • 将您的数据逻辑放入 ViewModel 类。ViewModel 应当充作您的 UI 控制器和应用其余部分的连接。请小心注意:获取数据(比如从网络下载)并不是 ViewModel 的责任,恰恰相反,ViewModel 应当调用合适的组件去获取数据,再将结果提供给 UI 控制器。
  • 使用数据绑定来在您的视图和 UI 控制器之间维持整洁的接口。这有利于让您的视图更具陈述性,而且让您在 UI 控制器中只需添加最少的更新逻辑。如果您更愿意使用 Java 语言来进行数据绑定,使用诸如 Butter Knife 的库可以避免八股代码而获得更好的抽象。
  • 如果您的 UI 很复杂,请考虑创建一个 presenter 类来处理 UI 修改。这可能会比较累人,但可以让您的 UI 组件更易于测试。
  • 避免在您的 ViewModel 中直接引用 View 或 Activity 等 context。如果 ViewModel 比其 activity 还活得长久(比如配置变更),您的 activity 就内存泄漏了,很难被 GC 得当回收。

能感知生命周期的组件的用例

能感知生命周期的组件能让许多用例中的生命周期管理变得简单。以下是几个例子:

  • 切换地理位置更新的精度。当您的应用可见时,启动高精度的地理位置更新;当您的应用进入后台时,切换成低精度的更新。LiveData 就是一个能感知生命周期的组件,让您的应用在用户移动的时候自动更新 UI。
  • 开始和停止视频缓冲。尽快开始视频缓冲,但把回放延迟到应用完全启动之后。您还可以使用这样的组件来在应用被销毁时停止视频缓冲。
  • 开始和停止网络连接。当应用在前台时,开启使用网络连接的流媒体;当应用在后台时自动暂停。
  • 暂停和恢复动画 drawable。当应用进入后台时,处理动画 drawable 的暂停;当应用回到前台时,恢复动画。

处理 onStop 事件

当 Lifecycle 属于一个 AppCompatActivity 或 Fragment,Lifecycle 的状态会变为 CREATED,而 ON_STOP 事件会在 AppCompatActivity 或 Fragment 的 onSaveInstanceState() 被调用时分发。

当一个 Fragment 或 AppCompatActivity 的状态在 onSaveInstanceState() 中保存时,直到 ON_START 触发之前,其 UI 被认为是不可变动的。试图在状态保存后更改 UI 可能会导致您的应用产生不一致的导航状态,这就是为什么当应用在状态保存之后试图执行 FragmentTransaction 时,FragmentManager 会抛出异常。欲了解更多信息,请参阅 commit()。

LiveData 提供了对这种边缘情况的开箱即用的支持:如果观察者所联系的 Lifecycle 连 STARTED 都不是的话,它就不会调用该观察者。其内部实现是先调用 isAtLeast() 来决定是否要触发其观察者。

悲剧的是,AppCompatActivity 的 onStop() 是在 onSaveInstanceState() 之后调用的,这导致了一段 UI 不允许改动、但 Lifecycle 还没进入 CREATED 状态的间隙。

为了防止这样的悲剧,beta2 及更低版本的 Lifecycle 将状态标记为 CREATED 但并不分发事件,这样一来任何检查当前状态的代码都会得到真正的值,尽管直到系统调用 onStop() 之前事件都没被分发。

二次悲剧的是,这种解决方案有两个主要的问题:

  • 在 23 及更低的 API 版本上,一个 activity 即使被另一个 activity 部分遮挡,Android 系统实际上也会保存其状态。换言之,Android 系统调用了 onSaveInstanceState(),但并不一定调用 onStop()。这导致了一个可能很长的间隙,期间观察者仍然认为生命周期是活动的,但其实 UI 状态不能改动。
  • 任何想要把类似行为暴露给 LiveData 的类都需要实现 beta2 或更低版本的 Lifecycle 所提供的变通方案。

注意:为了让上述流程更简单、向后兼容性更好,从 1.0.0-rc1 版本开始,Lifecycle 对象会被标记为 CREATED,而 ON_STOP 会在 onSaveInstanceState() 被调用时分发、毋须等待 onStop 方法被调用。这应该不太会影响您的代码,但您仍然需要了解,因为它和 26 及更高版本的 API 中 Activity 方法的调用顺序并不一致。

以上是来自官方文档的翻译,翻译地址见参考一。

参考

使用能感知生命周期的组件来处理生命周期

初学 Android 架构组件之 Lifecycle

Android官方架构组件Lifecycle:生命周期组件详解&原理分析

Jetpack中的Lifecycle

Android arch components 源码分析(2)—— Lifecycle

Android Architecture Component -- Lifecycle 浅析

生命周期组件 Lifecycle 源码解析(一)

生命周期组件 Lifecycle 源码解析(二)

Android架构之美-Lifecycle