记录一个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,重心放在 forwardPassbackwardPass ,他们统一调用的方法就是 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

这个时候点击下一步就是崩溃了。

修复方案

  1. 增加不是主线程 addObserer 检查(用于防止事情再次发生)
  2. 移动非主线程代码到主线程中

小结

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