记录一个LifeCycle 多线程使用导致的崩溃
关键字
- 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 检查(用于防止事情再次发生)
- 移动非主线程代码到主线程中
小结
条件矛盾大概率是线程问题,剩下就是怎么构造多线程修改条件。