关键字
- lifecycle
- 多线程
- java.lang.IllegalArgumentException
- bug
- android
- androidx
问题描述
在调用 getLifecycle().addObserver() 的时候报出这样的错误
java.lang.IllegalArgumentException
at androidx.lifecycle.LifecycleRegistry.upEvent(SourceFile:279)
at androidx.lifecycle.LifecycleRegistry.forwardPass(SourceFile:293)
at androidx.lifecycle.LifecycleRegistry.sync(SourceFile:333)
at androidx.lifecycle.LifecycleRegistry.addObserver(SourceFile:189)
问题定位
问题代码出现在这里 LifecycleRegisty 这个类中,代码如下
private static Event upEvent(State state) {
switch (state) {
case INITIALIZED:
case DESTROYED:
return ON_CREATE;
case CREATED:
return ON_START;
case STARTED:
return ON_RESUME;
case RESUMED:
throw new IllegalArgumentException();
}
throw new IllegalArgumentException("Unexpected state value " + state);
}
当传入的状态是 RESUMED 的时候可以就会抛出错误,而调用这个方法的代码如下
@Override
public void addObserver(LifecycleObserver observer) {
State initialState = mState == DESTROYED ? DESTROYED : INITIALIZED;
ObserverWithState statefulObserver = new ObserverWithState(observer, initialState);
ObserverWithState previous = mObserverMap.putIfAbsent(observer, statefulObserver);
if (previous != 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(mLifecycleOwner, upEvent(statefulObserver.mState));
popParentState();
// mState / subling may have been changed recalculate
targetState = calculateTargetState(observer);
}
if (!isReentrance) {
// we do sync only on the top level.
sync();
}
mAddingObserverCounter--;
}
因为在 State 是个枚举类型 ,RESUME 排在最后,所以是最大的
public enum State {
// 删除了无用注释
DESTROYED,
INITIALIZED,
CREATED,
STARTED,
RESUMED;
}
也就是说,以下代码中
while ((statefulObserver.mState.compareTo(targetState) < 0&& mObserverMap.contains(observer))) {
pushParentState(statefulObserver.mState);
statefulObserver.dispatchEvent(mLifecycleOwner, upEvent(statefulObserver.mState));
popParentState();
// mState / subling may have been changed recalculate
targetState = calculateTargetState(observer);
}
}
如果你想进入循环并且满足调用 upEvent() 发生崩溃的 statefulObserver.mState 值是不存在的,因为 upEvent()需要 statefulObserver.mState的值等于RESUMED , 但是 statefulObserver.mState.compareTo(targetState) < 0 这个刚好就不能是这个条件,RESUMED 是最大的 state 值,是不可能存在其他值比较之后小于0的。
当出现这种前后矛盾的时候,大概率就是多线程调用导致了。
这个时候我们就要开始找,还有什么别的方法会导致 statefulObserver.mState 的改变,通过 IDE 的 find usage 可以轻松找到 mState 修改只有下图中的两处

dispatchEvent 的使用

addObserver,重心放在 forwardPass 和 backwardPass ,他们统一调用的方法就是 sync,通过断点调试就能发现LifeCycleOwner 生命周期改变的时候会调用这个方法。也就是说,如果我使用非UI线程调用 addOboserver 同时改变生命周期就能达到崩溃的条件。
我们在两个地方设置断点,分别是


然后在 onResume 增加代码
override fun onResume(){
super.onResume()
Thread { lifecycle.addObserver(new ObserverImp()) }.start()
}
因为出错的状态是 RESUMED, 所以你只要 RESUMED 的时候加入 Oboserver 才能得到生命周期报错。操作路径是 App 后台返回前台显示,然后你就会看到

两个线程的显示不是一开始就有的,需要点多几下过,因为需要生命周期调用 addObserver 之后才会开始新线程
这个时候我们只需要操作 UI 线程停在 RESUME 即可,如下图

这个时候我们切换到另外一个线程, statefulObserver.mState 值就是 RESUME

修复方案
- 增加不是主线程 addObserer 检查(用于防止事情再次发生)
- 移动非主线程代码到主线程中
小结
条件矛盾大概率是线程问题,剩下就是怎么构造多线程修改条件。