Android Jetpack 之 Lifecycle - 4

444 阅读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 用变量 mAddingObserverCountermHandlingEvent 来判断是否处于重入状态。

其他 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 中直接引用 ViewActivity 等 context。如果 ViewModel 比其 activity 还活得长久(比如配置变更),您的 activity 就内存泄漏了,很难被 GC 得当回收。

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

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

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

处理 onStop 事件

Lifecycle 属于一个 AppCompatActivityFragmentLifecycle 的状态会变为 CREATED,而 ON_STOP 事件会在 AppCompatActivityFragmentonSaveInstanceState() 被调用时分发。

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

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

悲剧的是,AppCompatActivityonStop() 是在 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