關鍵字
- 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 檢查(用於防止事情再次發生)
- 移動非主執行緒程式碼到主執行緒中
小結
條件矛盾大概率是執行緒問題,剩下就是怎麼構造多執行緒修改條件。