☰
Android Lifecycle源码深度解析:状态机与事件分发机制全揭秘
2026/10/9 6:30:51 网站建设 项目流程

做过几年的Android开发,谁还没被生命周期折磨过?Activity的onCreate、onStart、onResume、onPause、onStop、onDestroy来回切换,Fragment更狠,十几个回调排着队等你处理。早起我都是手动在onStart里注册广播、在onStop里注销,在onResume里启动动画、在onPause里停止动画,代码里到处都是模板方法,稍微漏一个就会出问题。后来Google推出了Lifecycle组件,把生命周期管理从“人工维护”变成了“自动分发”,确实省心不少。但我觉得大部分人只停留在“会用”的层面——知道LifecycleOwner会自动派发事件,知道用注解@OnLifecycleEvent可以监听生命周期,可真要是问一句:LifecycleRegistry内部是怎么把ON_START派发到观察者手里的?状态机的高低是怎么定的?很多同学就答不上来了。

这篇文章我打算直接从源码层面,把Lifecycle这套机制彻底掰开揉碎。不讲使用规范的PPT,就盯住几个核心类:Lifecycle、LifecycleOwner、LifecycleRegistry、LifecycleObserver,把事件怎么来、状态怎么变、观察者怎么被通知到,一条线全部捋清楚。适合谁看?你已经能用Lifecycle写代码,但想知道内部原理的;或者面试前想临时抱佛脚,把生命周期这块讲出深度的;又或者你在自定义LifecycleOwner、排查“回调不执行”这类问题,希望有一个源码级的排查思路。不管哪种,这篇应该都能给你一点不一样的东西。

1. 源码视角下的Lifecycle整体架构

1.1 一次回调背后三个核心角色

Lifecycle这套框架,往大了说就三个角色。第一个是Lifecycle本身,它是一个抽象类,定义了addObserver和removeObserver两个抽象方法,还有一个getCurrentState方法。注意,Lifecycle在源码里是一个“抽象类”而不是接口,这个细节很多人没注意到。它之所以设计成抽象类,是因为官方后续可能需要往里面加非抽象方法,用抽象类比用接口更灵活,不会强迫所有下游实现类都去实现新方法。

第二个角色是LifecycleOwner,它更简单,就是个标记接口,里面只有一个方法:getLifecycle()。谁实现了它,谁就拥有了被观察的资格。Activity和Fragment都实现了这个接口,ViewModel之所以能拿到生命周期,也是因为它在创建的时候传入了一个LifecycleOwner,通过getLifecycle()把生命周期事件转发过去。

第三个角色就是核心中的核心:LifecycleRegistry。它是Lifecycle抽象类的具体实现类,所有状态管理、事件派发的逻辑都写在这个类里面。Activity和Fragment使用的也是这个类,只不过通过ReportFragment或者LifecycleCallbacks把它和真实的生命周期回调绑定在了一起。

观察者这侧,LifecycleObserver是一个空接口,没有任何方法。真正有用的是它的两个子接口:注解时代的老朋友@OnLifecycleEvent已经在新版本中被标记为废弃,官方现在推荐的是LifecycleEventObserver和DefaultLifecycleObserver。LifecycleEventObserver定义了一个onStateChanged(LifecycleOwner, Lifecycle.Event)方法,任何事件都会统一回调到这里;DefaultLifecycleObserver则针对每个生命周期事件定义了默认空实现的回调,重写对应方法即可精确监听。换句话说,你现在看到的所有生命周期回调,本质上都是onStateChanged这一个方法在不同事件参数下的分派结果。

1.2 状态和事件:先分清“现在在哪”和“要去哪”

要理解Lifecycle源码,有一个概念必须先掰清楚,就是State和Event的区别。

State描述的是“当前处在哪个状态”,它是一个静态的属性。源码中State是一个枚举,按定义顺序依次是:DESTROYED、INITIALIZED、CREATED、STARTED、RESUMED。这里有个细节,DESTROYED被定义在第一位,也就是枚举比较值最小,这是官方有意为之的。因为从比较逻辑来看,状态之间的大小关系正好对应生命周期的推进程度,DESTROYED最小,RESUMED最大。

Event描述的是“正在发生什么变化”,它是一个动作。比如ON_CREATE表示Activity创建了,ON_START表示即将进入可见状态。Lifecycle源码里有一个STATE_MAP和两组方法(downEvent、upEvent)专门用来在State和Event之间做换算。

这个设计跟生活中的“状态机”很像。你可以把生命周期看作一条从诞生到销毁的时间轴,State就是你在某个时间点上所处的位置,Event就是告诉你下一步该往哪个方向挪一挪的通知。源码里所有的状态流转逻辑,本质上都在这条时间轴上进行前后移动。

1.3 为什么设计成观察者模式而不是直接回调

老项目里常见的做法是三层嵌套:Activity直接调Presenter的onResume(),或者通过接口回调。这种写法最大的问题就是耦合。业务组件跟具体的生命周期宿主绑在了一起,测试的时候还得mock出来一个Activity,不好搞。

Lifecycle选择把“生命周期事件源”和“事件接收者”彻底解耦。LifecycleOwner只负责提供Lifecycle对象,LifecycleRegistry只负责保存观察者列表和当前状态,观察者只实现观察接口。三者之间通过addObserver订阅、通过事件分发通知,谁也不用关心对方的具体实现。翻译成大白话就是:我不在乎你是什么类型,只要你实现了LifecycleOwner,我就能注册观察者;我也不在乎观察者是谁,只要实现LifecycleObserver,就把事件推给你。

这种解耦带来一个直接的好处:任何实现LifecycleOwner的类都可以被复用。Fragment、Activity、甚至你自定义的一个ViewModel容器,都可以共享同一套生命周期逻辑。这也是为什么后来LiveData、lifecycleScope能放心地依赖Lifecycle——底层这一套观察者模式已经替它们把复杂的状态流转全部封装好了。

2. 状态机核心设计源码解析

2.1 关键源码:State枚举与事件映射表

先把最核心的几个定义搬出来看。

public enum State { DESTROYED, INITIALIZED, CREATED, STARTED, RESUMED; public boolean isAtLeast(@NonNull State state) { return compareTo(state) >= 0; } }

State是枚举,所以枚举的ordinal顺序直接决定大小关系。DESTROYED的ordinal是0,INITIALIZED是1,CREATED是2,STARTED是3,RESUMED是4。isAtLeast方法就是拿当前状态的ordinal和目标状态比较,达到或者超过才算成立。

然后是事件到状态的映射表STATE_MAP:

static Map<Event, State> STATE_MAP = new HashMap<>(); static { STATE_MAP.put(Event.ON_CREATE, State.CREATED); STATE_MAP.put(Event.ON_START, State.STARTED); STATE_MAP.put(Event.ON_RESUME, State.RESUMED); STATE_MAP.put(Event.ON_PAUSE, State.STARTED); STATE_MAP.put(Event.ON_STOP, State.CREATED); STATE_MAP.put(Event.ON_DESTROY, State.DESTROYED); }

这个映射表的意思很直白:某个事件发生之后,生命周期应该停留在哪个状态。ON_CREATE发生后状态变为CREATED,ON_START发生后状态变为STARTED,按直觉走就行;ON_PAUSE发生后状态回落到STARTED,ON_STOP之后回落到CREATED,ON_DESTROY之后直接到DESTROYED。这张表是整个状态机的地基,有了它,任何时候只要拿到一个事件,就能立刻算出事件发生后应该处于哪个状态。

2.2 downEvent与upEvent:状态转移的方向

除了映射表,源码还提供了两个方向不同的方法,用来计算从一个状态出发,下一个事件应该是什么。

static Event downEvent(@NonNull State state) { switch (state) { case INITIALIZED: throw new IllegalArgumentException(); case CREATED: return Event.ON_DESTROY; case STARTED: return Event.ON_STOP; case RESUMED: return Event.ON_PAUSE; default: throw new IllegalArgumentException(); } } static Event upEvent(@NonNull State state) { switch (state) { case INITIALIZED: case DESTROYED: return Event.ON_CREATE; case CREATED: return Event.ON_START; case STARTED: return Event.ON_RESUME; default: throw new IllegalArgumentException(); } }

这两段代码是后面所有状态迁移的基础逻辑。upEvent是从“更低状态”往“更高状态”推进时应该触发的事件:INITIALIZED往上推,得先发ON_CREATE;CREATED往上推,发ON_START;STARTED往上推,发ON_RESUME。downEvent则相反,从高处往低处退:RESUMED往下降,发ON_PAUSE;STARTED往下降,发ON_STOP;CREATED往下降,发ON_DESTROY。

这里有个特别容易被忽略的坑:downEvent在INITIALIZED状态下会直接抛IllegalArgumentException。为什么?因为State枚举里DESTROYED的ordinal比INITIALIZED还小,而INITIALIZED到DESTROYED之间并不需要经过任何事件,直接销毁了就完了。所以在源码内部,从INITIALIZED状态降到DESTROYED的处理逻辑是单独写在sync方法里的,不会走到downEvent这条分支。

2.3 有效状态转移图与事件合法性判断

把上面两个方法合并,就可以画出一张完整的“合法状态转移图”。这是Lifecycle源码中最重要的隐式规则:

INITIALIZED --ON_CREATE--> CREATED --ON_START--> STARTED --ON_RESUME--> RESUMED ^ | | | | --ON_DESTROY--> --ON_STOP--> --ON_PAUSE--> | DESTROYED STARTED

另外还有一个细节:事件能否合法触发,不仅取决于当前状态,还取决于“事件发生后目标状态能不能与当前状态有效兼容”。Lifecycle源码并没有提供显式的校验方法,它通过sync方法和ObserverWithState里的状态对比来隐式保证,如果事件对应的目标状态已经低于或等于当前状态,在某些流程下就直接跳过分发。

举个实际例子,如果你的Activity在onResume之后又通过某种外力手动调用了一次handleLifecycleEvent(ON_CREATE),Lifecycle不会把这个非法事件派发给观察者。源码内部getStateAfter方法会算出来,这个ON_CREATE的目标状态是CREATED,但当前状态已经是RESUMED,发生了“回退事件去往更小状态”的矛盾,不会再触发观察者回调。这种隐式防御就是状态机设计的价值所在,它替开发者挡掉了很多不必要的异常路径。

3. LifecycleRegistry事件分发机制解读

3.1 核心字段与数据结构的巧妙设计

LifecycleRegistry继承自Lifecycle抽象类,一个正常的类肯定要维护观察者列表和当前状态。它的核心字段如下:

private final WeakReference<LifecycleOwner> mLifecycleOwner; private int mState; private final FastSafeIterableMap<LifecycleObserver, ObserverWithState> mObserverMap; private boolean mHandlingEvent = false; private boolean mNewEventCollected = false; private int mStateChangeCounter = 0;

字段本身不难理解,但设计上有几个值得细品的地方。mLifecycleOwner是WeakReference,也就是弱引用。这个设计的目的很纯粹:防止LifecycleRegistry持有Activity的强引用导致内存泄漏。Activity销毁后,如果没有其他强引用持有它,GC就能正常回收它。这里有个细节,LifecycleRegistry构造时如果owner本身就是LifecycleOwner对象引用,它在处理各种事件时如果owner已经被回收,就要直接短路返回。

mObserverMap是FastSafeIterableMap,这是androidx.collection里的一个专用数据结构。它不是普通的LinkedHashMap或者ConcurrentHashMap,而是既支持快速迭代,又支持在迭代过程中安全移除的Map。为什么需要这种结构?因为Lifecycle在派发事件的过程中,观察者可能在里面新增或删除观察者,如果使用普通Map,很容易触发ConcurrentModificationException。后面我会单独讲这个数据结构。

3.2 addObserver流程:新观察者如何追上当前状态

观察者注册的核心方法是addObserver,这里面的逻辑有很多门道。

@Override public void addObserver(@NonNull LifecycleObserver observer) { State initialState = mState == DESTROYED ? DESTROYED : INITIALIZED; ObserverWithState statefulObserver = new ObserverWithState(observer, initialState); ObserverWithState previous = mObserverMap.putIfAbsent(observer, statefulObserver); if (previous != null) { return; } LifecycleOwner lifecycleOwner = mLifecycleOwner.get(); if (lifecycleOwner == null) { return; } boolean isReentrance = mAddingObserverCounter != 0 || mHandlingEvent; State targetState = calculateTargetState(observer); mAddingObserverCounter++; while ((statefulObserver.mState.compareTo(targetState) < 0 && !mObserverMap.contains(observer))) { pushParentState(statefulObserver.mState); statefulObserver.dispatchEvent(lifecycleOwner, upEvent(statefulObserver.mState)); popParentState(); targetState = calculateTargetState(observer); } if (!isReentrance) { sync(); } mAddingObserverCounter--; }

我把代码的关键流程简化一下。第一步,设置初始终状态。如果registry当前已经是DESTROYED,那么新观察者的初始状态就是DESTROYED;否则是INITIALIZED。也就是说,一个在Activity销毁之后注册的观察者,不会收到任何onCreate之类的事件。

第二步,把observer包装成ObserverWithState放进mObserverMap。注意,putIfAbsent如果发现同一个observer之前已经注册过了,就什么都不做直接返回。这是去重逻辑。同一个观察者实例重复注册,不会触发两次回调。

第三步,有一个while循环,它做的是“追赶状态”。想象一下这个场景:Activity已经执行到onStart了,状态是STARTED,这时候你才在onStart里addObserver注册一个新的观察者。如果新观察者的初始状态是INITIALIZED,那它永远无法收到ON_CREATE,因为它注册晚了,那个事件已经过去了。源码的处理方式很聪明:不能补发已经发生过的事件,但可以“按顺序模拟追赶过程”——新观察者虽然错过了ON_CREATE,但通过这个循环,一次补发ON_CREATE、ON_START,直到补发到最终状态STARTED。所以观察者注册之后,它的状态很快就能和registry的当前状态保持一致。这个机制,是新老观察者行为一致的关键。

3.3 sync()同步机制:forwardPass与backwardPass两趟遍历

addObserver流程走到最后,如果当前没有在处理事件,就会调用sync(),这是整个Lifecycle实现最核心的一段逻辑。

private void sync() { LifecycleOwner lifecycleOwner = mLifecycleOwner.get(); if (lifecycleOwner == null) { throw new IllegalStateException("LifecycleOwner of this LifecycleRegistry is already" + "garbage collected. It is too late to handle lifecycle event. Did you forget" + "to pass a LifecycleOwner when creating the LifecycleRegistry?"); } while (!isSynced()) { mStateChangeCounter++; if (mState.compareTo(mObserverMap.newest().getValue().mState) < 0) { backwardPass(lifecycleOwner); } Entry<LifecycleObserver, ObserverWithState> newest = mObserverMap.newest(); if (!mNewEventCollected && newest != null && newest.getValue().mState.compareTo(mState) > 0) { forwardPass(lifecycleOwner); } mStateChangeCounter--; } }

isSynced()的判断逻辑是:所有观察者中最新的那个观察者的状态等于registry的mState。如果相等,说明同步完成了,直接退出循环。如果不等,就有两种可能:观察者状态比mState高,说明观察者跑得快了,需要降级,走backwardPass;观察者状态比mState低,说明观察者落后了,需要升级,走forwardPass。

forwardPass是从mObserverMap的头部开始,沿着链表往尾部遍历,对每个观察者调用upEvent,把观察者状态一路向上“推”到与registry状态一致。backwardPass反着来,从尾部往前遍历,用downEvent把观察者状态一路“降”到与registry状态一致。这里最需要注意的地方是:forwardPass的时候,observer虽然“追”上来了,但registry的mState其实是先被降级或者升级完成的状态,而观察者的状态是逐个更新的。整个同步过程是强制收敛的,所有观察者最终都会停在registry当前mState的同一水平线上。

打个比方:Activity从STARTED升到RESUMED时,registry的mState先更新为RESUMED,然后forwardPass逐个通知所有观察者,让每个观察者的状态也都升到RESUMED。从观察者视角来看,它们收到的是ON_RESUME事件。升的过程是一层一层盖上去的,降的过程是一层一层拆下来的,绝不会出现“直接从RESUMED跳到DESTROYED”这种跳层式通知。

4. 重入安全与异常防线

4.1 mHandlingEvent与mNewEventCollected:防止事件嵌套

Lifecycle的事件分发虽然是同步的,但观察者在回调里是可以继续操纵Lifecycle的。比如你在onStateChanged回调里又调用了handleLifecycleEvent,或者removeObserver,再或者addObserver。这些操作在源码看来,都属于“在事件处理过程中再次修改状态或观察者列表”的非法重入场景。

为了避免这种场景搞乱状态,Lifecycle设计了一道防线,用两个标志位来配合:mHandlingEvent和mNewEventCollected。先看handleLifecycleEvent方法:

public void handleLifecycleEvent(@NonNull Lifecycle.Event event) { State next = getStateAfter(event); moveToState(next); } private void moveToState(State next) { if (mState == next) { return; } mState = next; if (mHandlingEvent || mAddingObserverCounter != 0) { mNewEventCollected = true; return; } mHandlingEvent = true; sync(); mHandlingEvent = false; }

这段代码的精髓在于:moveToState先更新mState,把状态变到目标状态,然后判断当前是否正在处理事件(mHandlingEvent是否为true),或者当前是否在添加观察者(mAddingObserverCounter是否为0)。如果是,说明事件来晚了,不能马上同步,只能先记一个标志mNewEventCollected = true,等当前事件处理完再补同步。如果当前没有在处理其他事件,才进入正式的mHandlingEvent = true; sync(); mHandlingEvent = false;流程。

mNewEventCollected这个标志一旦被置true,sync()在退出while循环前就会看到它,然后继续循环,再来一轮sync。等于说,嵌套事件被安排到下一轮队列执行了,不会直接递归地打乱当前同步过程。这套机制本质上就是一个“事件排队”设计,保证了事件处理的单线程顺序性。

为什么要这么设计?因为如果不加这道防线,可能出现这种情况:你在ON_RESUME回调里又调用moveToState去处理ON_PAUSE,此时mState已经是RESUMED,又去派发ON_PAUSE给观察者,观察者在ON_PAUSE回调里再调用ON_STOP……一层套一层,递归深度失控,状态机直接崩溃。有了mHandlingEvent挡一层,同步逻辑永远不会在自己执行的过程中被再次调用。

4.2 ObserverWithState:观察者的私人状态管理器

mObserverMap里装着的不是LifecycleObserver本身,而是一个包装类ObserverWithState。这个类很小,但作用关键:

static class ObserverWithState { State mState; LifecycleEventObserver mLifecycleObserver; ObserverWithState(LifecycleObserver observer, State initialState) { mLifecycleObserver = Lifecycling.lifecycleEventObserver(observer); mState = initialState; } void dispatchEvent(LifecycleOwner owner, Event event) { State newState = event.getTargetState(); mState = newState; mLifecycleObserver.onStateChanged(owner, event); mState = newState; } }

ObserverWithState维护着两个东西:这个观察者当前处于哪个状态mState,以及它应该以什么方式接收事件mLifecycleObserver。Lifecycling.lifecycleEventObserver是个工厂方法,它会根据你传入的观察者类型做适配:如果实现了LifecycleEventObserver,直接用;如果实现了DefaultLifecycleObserver,就包一层DefaultLifecycleObserverAdapter;如果只用了@OnLifecycleEvent注解,就反射生成一个注解处理适配器。

dispatchEvent的核心是先计算出事件的目标状态,把mState更新到目标状态,再回调通知观察者。这个顺序千万不要搞反,只有先更新mState再通知观察者,观察者回调里再去查询getCurrentState()才能拿到最新值。很多开发者会疑惑:为什么在ON_RESUME回调里,getCurrentState()已经是RESUMED,而不是还停留在旧的STARTED?答案就在这里,源码先用newState更新状态,再回调。

4.3 FastSafeIterableMap:迭代过程中安全移除的秘密

Lifecycle的任务是不断地遍历mObserverMap,给每个观察者派发事件。如果在遍历过程中,某个观察者在回调里把自己remove掉了,使用普通集合必然会出现ConcurrentModificationException。FastSafeIterableMap就是为了解决这个问题的专用结构。

它内部基于双向链表实现,遍历时使用的是每次都会重新创建的Iterator,这个Iterator在创建时会保存当前遍历位置。当你在回调里remove掉当前观察者时,如果remove的是正在遍历的那个节点,迭代器可以继续指向下一个节点,不会抛异常。另外它在remove时并不是直接从链表中断开,而是先做一个标记,等迭代游标推过之后真正摘除。

这个设计从一定程度避免了同步代码块带来的锁开销,同时保证了遍历稳定。如果你以后需要自研类似的观察者框架,这个思路值得直接抄。

4.4 Holder类:经典的单例写法

在Lifecycle源码里还有一个小但值得一提的点:State和Event这些类本身没有单例设计,但Lifecycling、GeneratedAdapter等工具类中大量使用了“Holder模式”。比如Lifecycling有一个:

private static class GeneratedAdapterClassHolder { static final Map<Class<?>, Boolean> sClassToGeneratedAdapter = new ArrayMap<>(); }

这种写法充分利用了类加载机制保证holder只在首次访问的时候初始化,线程安全又延迟加载,比双重检查锁要简洁得多。如果你看源码只盯着LifecycleRegistry看,可能会忽略这些小细节,但它们其实都是Google在工程实践里沉淀下来的套路,单独抄下来在别的地方用也很值。

5. 源码级避坑指南与排查实录

5.1 观察者回调不触发,先怀疑注册时机和事件合法性

我在实际开发中遇到的第一个“回调不执行”的问题,就是在Activity.onResume之后addObserver一个监听生命周期的观察者,指望它收到ON_RESUME。结果它只收到了ON_CREATE的“追赶事件”,并没有收到预期的ON_RESUME。

看源码就明白了:addObserver执行时,同步过程只保证把观察者的状态“追”到当前状态,事件补发的流程是严格按顺序来的。如果当前状态是RESUMED,追赶循环依次补发ON_CREATE、ON_START、ON_RESUME,但补发过程只是回到当前状态,并不会再次派发“当前所在状态本身”的事件。也就是说,如果你在Activity已经处于RESUMED状态后再注册观察者,你会错过ON_CREATE和ON_START回调里的逻辑(因为这些事件已经过去了),但你仍然会收到一次ON_RESUME的追赶回调。很多项目里出现的“为什么刚注册就收到了一次ON_CREATE或ON_RESUME”,其实就是在追赶状态。

如果你希望观察者只监听此后“未来发生”的事件,不想要追赶逻辑,就需要在addObserver之前判断getCurrentState(),或者把整个注册放到更早的生命周期节点。这个问题在设计Framework层生命周期观察者时特别容易踩,一个小改动,就可能让观察者多收到一堆莫名其妙的事件。

5.2 IllegalStateException:LifecycleOwner被GC回收

LifecycleRegistry的sync方法里有一个风险点:如果mLifecycleOwner已经被回收,会直接抛出IllegalStateException,提示“It is too late to handle lifecycle event”。

这种情况在什么场景发生?一个典型场景是ViewModel里持有了LifecycleRegistry,但是Activity销毁之后没有清掉引用,而某个后台线程还在往这个registry里post事件。此时弱引用拿不到owner,就会抛异常。

解决办法通常是:线程回调里先判断registry是否仍持有强引用的LifecycleOwner,或者干脆用LiveData这种自带lifecycle感知能力的组件做数据传递,避免自己往registry手动投递事件。另一个思路是把手动管理LifecycleRegistry的类放在正确的生命周期内注册和注销,不要用静态变量持有它。

5.3 内存泄漏:观察者自己成了定时的强引用

很多人只知道“注册了就要注销”,但可能没想过,观察者本身如果被注册进一个生命周期比它长的对象里,也可能造成泄漏。比如你用observeForever订阅一个无限循环的事件源,返回的observer在Activity销毁后依然存活,就会把整个Activity的引用链给拽住。

源码层面看,LifecycleRegistry内部对owner用的是弱引用,但对observer用的是强引用(mObserverMap的value持有observer),所以observer一旦注册进去,在removeObserver之前,它不会被自动清除。如果你把一个Activity内部的匿名Observer注册到Application级别的LifecycleOwner,这个Observer会隐式持有外部Activity的引用,Activity就无法被回收。

养成习惯就行:所有跟UI相关的观察者,绑定到UI组件自己实现的LifecycleOwner上,或者使用lifecycleScope自动取消,尽量避免observeForever。如果实在要用,在onDestroy里记得removeObserver成对释放。

5.4 多线程环境:状态变更必须回到主线程

Lifecycle的事件分发默认是同步、单线程的。源码里并没有做线程切换,addObserver、handleLifecycleEvent这些都要求调用方保证在同一线程执行。如果你在后台线程里给LifecycleRegistry投递了一个事件,再回到主线程又投递一个事件,状态顺序会变得乱七八糟,因为mState是一个普通字段,没有线程安全保护,两个线程同时写会有竞态。

我在自定义LifecycleOwner时就遇到过这个问题:子线程数据加载完成后直接调lifecycleRegistry.handleLifecycleEvent(ON_START),跟主线程Activity原本的ON_START事件撞在一起,状态一会CREATED一会STARTED,回调频次错乱。后来统一改成在主线程用Handler.post执行所有Lifecycle操作,问题就消失了。这算是踩坑之后的血泪总结。

5.5 观察者先于owner销毁执行,界面更新成空指针

还有一个很隐蔽的问题:如果观察者在Activity执行onDestroy之后依然存活(比如被外部对象持有),它的回调虽然会因为owner是弱引用而拿不到,但在某些场景下,观察者内部持有的View引用已经无效了,再调setText之类的方法就会崩。

所以写观察者时,回调里务必加一个安全性判断:owner.getLifecycle().getCurrentState() != DESTROYED,或者直接让回调所在类实现LifecycleEventObserver,在onStateChanged里先判断状态再更新UI。把这个判断写好,能帮你规避大量因为生命周期“晚半拍”造成的空指针崩溃。

6. 从源码理解实际开发最佳实践

6.1 为什么官方现在推荐使用DefaultLifecycleObserver

@OnLifecycleEvent注解的方式在旧版本中广泛使用,但它有两个硬伤。第一,它依赖反射,运行时通过注解处理器在类里生成适配代码,新增一种Event类型就要额外处理;第二,注解的方式是“聚合式”的,一个类可以同时给多个方法标注不同事件,这在源码处理上很绕,需要额外判断Method是否带参数等。

而DefaultLifecycleObserver是更现代的接口回调模式,每个生命周期事件对应一个默认空实现的方法,编译期类型安全,反射开销为零,Android Studio还能直接帮你补全方法。源码层面,Lifecycling.lifecycleEventObserver会对实现了DefaultLifecycleObserver的类做直接适配,不走反射。所以新项目一律用这个接口,几乎是一面倒的优势。

6.2 自定义LifecycleOwner的正确姿势

有时候你需要让自己的类也成为LifecycleOwner,比如自研的播放器或者定时器类。源码层面的正确做法是:类实现LifecycleOwner接口,内部用一个LifecycleRegistry来管理状态,并对外暴露addObserver等方法。

创建LifecycleRegistry时,需要传入当前类的实例作为LifecycleOwner的弱引用持有者。每次状态变化时调用handleLifecycleEvent来派发事件。这里必须确保所有的事件调用都在同一线程,通常是主线程。一个精简的模板如下:

public class MyLifecycleOwner implements LifecycleOwner { private final LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this); @NonNull @Override public Lifecycle getLifecycle() { return mLifecycleRegistry; } void onStart() { mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START); } void onStop() { mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP); } void onDestroy() { mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY); mLifecycleRegistry.removeObserver(observer); // 成对清理 } }

这个模板直接抄就能用。注意一个容易踩的点:handleLifecycleEvent(ON_DESTROY)之后,通常还要手动调removeObserver,把所有观察者清掉,否则observer列表里残留的观察者虽然不会收到新事件,但会持续占用内存引用。

6.3 与LiveData和lifecycleScope的联动机制

LiveData之所以能自动感知生命周期,底层也是在ObserverWrapper里持有了LifecycleOwner,并且通过shouldBeActive()判断当前状态是否为STARTED或RESUMED。这个判断本质上就是拿State枚举的compareTo在做比较,达到STARTED或以上才算激活状态。

lifecycleScope则利用Dispatchers.Main.immediate和Lifecycle的当前状态,实现了一种“挂起直到下一个生命周期事件”的机制。比如repeatOnLifecycle(State.STARTED)这个扩展函数,它内部会监听ON_START和ON_STOP事件,在ON_START时启动协程、ON_STOP时取消协程。理解了Lifecycle底层,你就能猜到它也是用addObserver注册了一个LifecycleEventObserver,只是把回调包装成了挂起函数的形式。

6.4 自定义状态:尽量少用注解,改用事件驱动

最后一个小建议:在复杂业务里,与其自己手动维护一堆状态布尔值,不如把数据来源和UI状态都建模成“事件驱动”。Lifecycle本身就是一个事件驱动的状态机,你可以在观察者的回调里构建自己的业务状态模型,比如“已完成加载”“正在播放”“刚刚暂停”等,每次事件发生后,用copy或者新的数据类替换旧状态。这样写出来的代码天然具备生命周期感知能力,不会出现UI状态和生命周期状态分叉的问题。源码里那种“先更新状态再回调通知”的顺序,在业务侧同样值得借鉴——永远把状态变更放在通知之前。

结尾

到现在为止,Lifecycle这套框架的主链路已经梳理完了:State定义方向上的一套时间轴,Up/Down事件确定怎么挪动,LifecycleRegistry承载观察者列表和当前状态,addObserver负责注册与追赶,sync负责分发与收敛,再加上mHandlingEvent、mNewEventCollected这些重入安全防线,整体就是一个非常经典的事件驱动状态机模型。

我个人在实际看源码的过程中,最大的体会是:Lifecycle真正了不起的地方不在于代码写得有多炫,而在于它把一个极其容易出错的生命周期管理问题,用一套极少的状态定义和事件机制,拆成了肉眼可见的确定性流程。你看完源码再回头写业务,碰到“回调不执行”“状态错乱”这类问题,脑海里就能立刻浮现出那几张函数调用图,排查问题的速度完全不在一个量级。

最后再分享一个小技巧:如果你也想彻底掌握这套机制,最好的办法不是反复读源码,而是跟着自己写一个简化版LifecycleRegistry——只保留DESTROYED到RESUMED四个状态,实现addObserver和handleLifecycleEvent,跑几个单元测试看看不同注册顺序下观察者的接收顺序是否符合预期。这个过程走完,源码里那些字段和方法的用意,你会比背十遍文章都记得牢。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询