animateValueAsState 补间崩溃(AnimationVector 不能包含 NaN)

问题描述 投票:0回答:2

无法在本地重现,在 firebase 上看到多次崩溃。它似乎对不同设备和 Android 版本的不同用户产生一致的影响。

堆栈跟踪没有给出任何迹象表明这是我们组件的问题,除了动画的持续时间之外,我尝试进行一些挖掘但无法重现。

下面是组件上类似编码的代码部分,我们在其中找到了崩溃时的 anaimationDuration 值:

@Composable
fun SomeComposable(
   someData: SomeData,
   modifier: Modifier = Modifier
) {
    AnimatedVisibility(
        modifier = modifier,
        visible = ...
    ) {

        val progressValue = remember(someData) {
            (someData.value1.toFloat() / someData.value2.toFloat()) * 100
        }

        val someDataAnimation by animateFloatAsState(
            targetValue = progressValue,
            animationSpec = tween(
                durationMillis = 700 <- duration value from a stacktrace
            )
        )

        val angle = remember(someDataAnimation) {
            someDataAnimation * 360 / 100
        }

        Box(...) {
            Canvas(...) {
                drawArc(
                   ...,
                   sweepAngle = angle,
                   ...
                )
            }
        }
    }
}

堆栈跟踪:

java.lang.IllegalStateException: AnimationVector cannot contain a NaN. AnimationVector1D: value = NaN. Animation: TargetBasedAnimation: NaN -> 41.12,initial velocity: AnimationVector1D: value = 0.0, duration: 700 ms,animationSpec: androidx.compose.animation.core.VectorizedTweenSpec@3d4a9b6, playTimeNanos: 0
    androidx.compose.animation.core.TargetBasedAnimation.getValueFromNanos(Animation.kt:242)
    androidx.compose.animation.core.SuspendAnimationKt.animate(SuspendAnimation.kt:233)
    androidx.compose.animation.core.Animatable$runAnimation$2.invokeSuspend(Animatable.kt:305)
    androidx.compose.animation.core.Animatable$runAnimation$2.invoke(:8)
    androidx.compose.animation.core.Animatable$runAnimation$2.invoke(:2)
    androidx.compose.animation.core.MutatorMutex$mutate$2.invokeSuspend(InternalMutatorMutex.kt:119)
    androidx.compose.animation.core.MutatorMutex$mutate$2.invoke(:8)
    androidx.compose.animation.core.MutatorMutex$mutate$2.invoke(:4)
    kotlinx.coroutines.intrinsics.UndispatchedKt.startUndispatchedOrReturn(Undispatched.kt:89)
    kotlinx.coroutines.CoroutineScopeKt.coroutineScope(CoroutineScope.kt:264)
    androidx.compose.animation.core.MutatorMutex.mutate(InternalMutatorMutex.kt:112)
    androidx.compose.animation.core.MutatorMutex.mutate$default(InternalMutatorMutex.kt:109)
    androidx.compose.animation.core.Animatable.runAnimation(Animatable.kt:295)
    androidx.compose.animation.core.Animatable.animateTo(Animatable.kt:238)
    androidx.compose.animation.core.Animatable.animateTo$default(Animatable.kt:225)
    androidx.compose.animation.core.AnimateAsStateKt$animateValueAsState$3$1.invokeSuspend(AnimateAsState.kt:426)
    kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
    androidx.compose.ui.platform.AndroidUiDispatcher.performTrampolineDispatch(AndroidUiDispatcher.android.kt:81)
    androidx.compose.ui.platform.AndroidUiDispatcher.access$performTrampolineDispatch(AndroidUiDispatcher.android.kt:41)
    androidx.compose.ui.platform.AndroidUiDispatcher$dispatchCallback$1.run(AndroidUiDispatcher.android.kt:57)
    android.os.Handler.handleCallback(Handler.java:942)
    android.os.Handler.dispatchMessage(Handler.java:99)
    android.os.Looper.loopOnce(Looper.java:226)
    android.os.Looper.loop(Looper.java:313)
    android.app.ActivityThread.main(ActivityThread.java:8757)
    java.lang.reflect.Method.invoke(Method.java:0)
    com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571)
    com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1067)

版本:

Jetpack Compose version: compiler 1.4.0, compose-bom:2023.01.00
Android Studio Build: any
Kotlin version: 1.8.0

此外,我在 Google Issue Tracker 中打开了issue

我尝试做一些研究,但我发现 NaN 值是浮点/双零除以浮点/双零的结果。

当然,我尝试将progressValue设置为NaN或零,但没有任何效果

此外,这次崩溃在某种程度上与以下问题

有关
android kotlin android-jetpack-compose android-animation
2个回答
0
投票

问题是你的

progressValue
变成了
NaN
(如堆栈跟踪所示)。这通常发生在我们例如除以 0。

一种解决方案是用

someData.valueX.toFloat()
调用包装您的
max(1, someData.value1.toFloat())
,这样您就永远不会除以 0。


0
投票

已修复

在任何 Compose 动画进度值中,请确保该值在运行时不能为 NaN。由于某种原因,如果您将 NaN 直接传递给可组合项的 targetValue,则不会触发崩溃,但在手动检查

progressValue
中的分频器值后,崩溃随着时间的推移从 crashlytics 中消失:

val progressValue = remember(someData) {
    val divisor = someData.value2.toFloat()
    ((someData.value1.toFloat() / divisor) * 100).takeIf { divisor != 0f } ?: 0f
}
© www.soinside.com 2019 - 2024. All rights reserved.