安卓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; } }
问题分析与结论
这个崩溃确实和启动页退出动画的实现逻辑直接相关,问题根源如下:
核心触发原因:
崩溃栈显示系统在ActivityThread.syncTransferSplashscreenViewTransaction中调用SurfaceControl.Transaction.hide()时,SurfaceControl对象已为null。这是因为启动页的Surface资源被提前释放,但系统仍尝试对其进行操作。代码中的问题点:
在退出动画的实现里,启动属性动画后立刻调用了splashScreenView.clearAnimation()——这个操作会直接终止当前的属性动画,提前触发onAnimationEnd回调,进而调用splashScreenViewProvider.remove()释放启动页的SurfaceControl。
而此时系统可能还在处理启动页的Surface关联逻辑,后续系统尝试操作已被释放的SurfaceControl时,就会抛出空指针异常。随机触发的原因:
崩溃属于竞态条件问题,只有当clearAnimation()的执行时机刚好和系统启动页的Surface生命周期管理逻辑冲突时才会触发,因此表现为随机出现。
修复方案
- 移除多余的
clearAnimation()调用:删除splashScreenView.clearAnimation();这一行,让属性动画自然执行完毕后再触发onAnimationEnd回调,调用remove()释放资源,确保动画逻辑和系统启动页生命周期同步。 - 增加状态校验(可选):在
onAnimationEnd中调用remove()前,可先通过splashScreenView.isAttachedToWindow()判断View是否仍处于有效状态,避免对已销毁的View进行操作。
内容的提问来源于stack exchange,提问作者jyothihayathi g
相关产品推荐
相关产品推荐

