You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

安卓SurfaceControl空指针崩溃求助:闪屏动画随机崩溃排查

启动页动画相关空指针崩溃排查

用户问题

捕获到如下随机触发的崩溃日志,无法定位问题,咨询该问题是否与动画相关,若相关,问题出在哪里?

崩溃日志

Fatal Exception: java.lang.NullPointerException: Attempt to invoke direct method 'void android.view.SurfaceControl.checkNotReleased()' on a null object reference
       at android.view.SurfaceControl.-$$Nest$mcheckNotReleased()
       at android.view.SurfaceControl$Transaction.checkPreconditions(SurfaceControl.java:2941)
       at android.view.SurfaceControl$Transaction.hide(SurfaceControl.java:3124)
       at android.app.ActivityThread.syncTransferSplashscreenViewTransaction(ActivityThread.java:4360)
       at android.app.ActivityThread.-$$Nest$msyncTransferSplashscreenViewTransaction()
       at android.app.ActivityThread$1.onDraw(ActivityThread.java:4337)
       at android.view.ViewTreeObserver.dispatchOnDraw(ViewTreeObserver.java:1132)
       at android.view.ViewRootImpl.draw(ViewRootImpl.java:4838)
       at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:4609)
       at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:3775)
       at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:2469)
       at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:9484)
       at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1405)
       at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1413)
       at android.view.Choreographer.doCallbacks(Choreographer.java:1040)
       at android.view.Choreographer.doFrame(Choreographer.java:930)
       at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1388)
       at android.os.Handler.handleCallback(Handler.java:942)
       at android.os.Handler.dispatchMessage(Handler.java:99)
       at android.os.Looper.loopOnce(Looper.java:240)
       at android.os.Looper.loop(Looper.java:351)
       at android.app.ActivityThread.main(ActivityThread.java:8423)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:584)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1013)

相关代码(SplashscreenPlugin.java)

private void setupSplashScreen(SplashScreen splashScreen) {
    // Setup Splash Screen Delay
    splashScreen.setKeepOnScreenCondition(() -> keepOnScreen);

    // auto hide splash screen when custom delay is defined.
    if (autoHide && delayTime != DEFAULT_DELAY_TIME) {
        Handler splashScreenDelayHandler = new Handler();
        splashScreenDelayHandler.postDelayed(() -> keepOnScreen = false, delayTime);
    }

    // auto hide splash screen with default delay (-1) delay is controlled by the
    // `onPageFinished` message.

    // If auto hide is disabled (false), the hiding of the splash screen must be determined &
    // triggered by the front-end code with the `navigator.splashscreen.hide()` method.

    // Setup the fade
    splashScreen.setOnExitAnimationListener(new SplashScreen.OnExitAnimationListener() {
        @Override
        public void onSplashScreenExit(@NonNull SplashScreenViewProvider splashScreenViewProvider) {
            View splashScreenView = splashScreenViewProvider.getView();

            splashScreenView
                .animate()
                .alpha(0.0f)
                .setDuration(isFadeEnabled ? fadeDuration : 0)
                .setStartDelay(isFadeEnabled ? 0 : fadeDuration)
                .setInterpolator(new AccelerateInterpolator())
                .setListener(new AnimatorListenerAdapter() {
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        super.onAnimationEnd(animation);
                        splashScreenViewProvider.remove();
                    }
                }).start();
            splashScreenView.clearAnimation();
        }
    });
}

private void attemptCloseOnPageFinished() {
    if (autoHide && delayTime == DEFAULT_DELAY_TIME) {
        keepOnScreen = false;
    }
}

问题分析与结论

这个崩溃确实和启动页退出动画的实现逻辑直接相关,问题根源如下:

  1. 核心触发原因:
    崩溃栈显示系统在ActivityThread.syncTransferSplashscreenViewTransaction中调用SurfaceControl.Transaction.hide()时,SurfaceControl对象已为null。这是因为启动页的Surface资源被提前释放,但系统仍尝试对其进行操作。

  2. 代码中的问题点:
    在退出动画的实现里,启动属性动画后立刻调用了splashScreenView.clearAnimation()——这个操作会直接终止当前的属性动画,提前触发onAnimationEnd回调,进而调用splashScreenViewProvider.remove()释放启动页的SurfaceControl。
    而此时系统可能还在处理启动页的Surface关联逻辑,后续系统尝试操作已被释放的SurfaceControl时,就会抛出空指针异常。

  3. 随机触发的原因:
    崩溃属于竞态条件问题,只有当clearAnimation()的执行时机刚好和系统启动页的Surface生命周期管理逻辑冲突时才会触发,因此表现为随机出现。

修复方案

  • 移除多余的clearAnimation()调用:删除splashScreenView.clearAnimation();这一行,让属性动画自然执行完毕后再触发onAnimationEnd回调,调用remove()释放资源,确保动画逻辑和系统启动页生命周期同步。
  • 增加状态校验(可选):在onAnimationEnd中调用remove()前,可先通过splashScreenView.isAttachedToWindow()判断View是否仍处于有效状态,避免对已销毁的View进行操作。

内容的提问来源于stack exchange,提问作者jyothihayathi g

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 12:50:58