记录一个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. 移动非主线程代码到主线程中

小结

条件矛盾大几率是线程问题,剩下就是怎么构造多线程修改条件。

相关文章
相关标签/搜索