記錄一個LifeCycle 錯誤使用致使 的BUG

關鍵字

  • lifecycle
  • 多線程
  • java.lang.IllegalArgumentException
  • bug
  • android
  • androidx

問題描述

在調用 getLifecycle().addObserver() 的時候報出這樣的錯誤java

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 這個類中,代碼以下android

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 的時候能夠就會拋出錯誤,而調用這個方法的代碼以下bash

@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;
    }

也就是說,如下代碼中ide

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的。spa

當出現這種先後矛盾的時候,大機率就是多線程調用致使了線程

這個時候咱們就要開始找,還有什麼別的方法會致使 statefulObserver.mState 的改變,經過 IDE 的 find usage 能夠輕鬆找到 mState 修改只有下圖中的兩處
6384ff7d357422f23a0885cc902667be
咱們忽略構建方法(由於每次構建的對象不同,不可能同時改),專心再找 dispatchEvent 的使用
acb9ea9cd1198f73a407ca56509df041
排除 addObserver,重心放在 forwardPassbackwardPass ,他們統一調用的方法就是 sync,經過斷點調試就能發現LifeCycleOwner 生命週期改變的時候會調用這個方法。也就是說,若是我使用非UI線程調用 addOboserver 同時改變生命週期就能達到崩潰的條件調試

咱們在兩個地方設置斷點,分別是
84dfd90a2134d0398b70b44a91bb032f
2d8350fe490a15c1d0238e778d6b351ecode

而後在 onResume 增長代碼server

override fun onResume(){
    super.onResume()
    Thread { lifecycle.addObserver(new ObserverImp()) }.start()
}

由於出錯的狀態是 RESUMED, 因此你只要 RESUMED 的時候加入 Oboserver 才能獲得生命週期報錯。操做路徑是 App 後臺返回前臺顯示,而後你就會看到
2996f302873fe06a420b6adc9d7286cc

兩個線程的顯示不是一開始就有的,須要點多幾下過,由於須要生命週期調用 addObserver 以後纔會開始新線程

這個時候咱們只須要操做 UI 線程停在 RESUME 便可,以下圖
58424558ab9effce44b3f8bf32b93957

這個時候咱們切換到另一個線程, statefulObserver.mState 值就是 RESUME
c58ffa7b0ca6e23941a87d9633190668
這個時候點擊下一步就是崩潰了。

修復方案

  1. 增長不是主線程 addObserer 檢查(用於防止事情再次發生)
  2. 移動非主線程代碼到主線程中

小結

條件矛盾大機率是線程問題,剩下就是怎麼構造多線程修改條件。

相關文章
相關標籤/搜索