Jetpack Compose Canvas — drawForPersistence вызывается для ссылки на нулевой объект

1
4

Crashlytics сообщает о странной ошибке, по-видимому, приложение никогда не вылетало во время разработки. Мне не удалось найти в сети ни одного случая возникновения этой (точной) ошибки. У нас есть Composable, который рисует дугу с некоторыми данными, извлеченными из модели представления. Ни один из данных не является пустым, все имеют значения по умолчанию, поэтому я думаю, что ошибка null не связана с этим. Один зарегистрированный случай этой ошибки произошел, когда пользователь понизил разрешение на местоположение с Все время до Во время использования (даже несмотря на то, что приложение было закрыто этим действием системой).

Вот Composable с использованием Canvas:

Fatal Exception: java.lang.NullPointerException: Attempt to invoke virtual method 'void androidx.compose.ui.graphics.layer.GraphicsLayer.drawForPersistence$ui_graphics_release(androidx.compose.ui.graphics.Canvas)' on a null object reference

at androidx.compose.ui.graphics.layer.LayerManager.persistLayers(LayerManager.android.kt:116)

at androidx.compose.ui.graphics.layer.LayerManager.updateLayerPersistence(LayerManager.android.kt:136)

at androidx.compose.ui.graphics.AndroidGraphicsContext$componentCallback$1$onTrimMemory$1.onPreDraw(AndroidGraphicsContext.android.kt:79)

at android.view.ViewTreeObserver.dispatchOnPreDraw(ViewTreeObserver.java:1204)

at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:4739)

at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:3302)

at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:11362)

at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1689)

at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1698)

at android.view.Choreographer.doCallbacks(Choreographer.java:1153)

at android.view.Choreographer.doFrame(Choreographer.java:1079)

at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1646)

at android.os.Handler.handleCallback(Handler.java:958)

at android.os.Handler.dispatchMessage(Handler.java:99)

at android.os.Looper.loopOnce(Looper.java:230)

at android.os.Looper.loop(Looper.java:319)

at android.app.ActivityThread.main(ActivityThread.java:8919)

at java.lang.reflect.Method.invoke(Method.java)

at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:578)

at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1103) 
@Composable
private fun DrawArc(
    progress: ArcScoreConfig,
    progressColor: Color,
    strokeWidth: Dp,
    endCircleColor: Color = DesignTokenColor.colorSemanticDatavizIndicator,
    circularEnd: Boolean = false,
    animate: Boolean = true,
) {
    val target: MutableFloatState = rememberSaveable(progress.score) { 
mutableFloatStateOf
(0f) }
    //animation for the arc, arc is animated fairly simple
    //we draw arc using percentages of the float
    //so if the the score is 83 we multiply that we rising value
    //of our "target" (0-1), at all times arc (and circle) are just relatively
    //scaled down (by their angle). As the float value is rising, so is arc's end
    //angle/
    val animation: Float by animateFloatAsState(
        targetValue = target.floatValue,
        animationSpec = 
tween
(
            durationMillis = DiscountArcProgressConfig.animationDuration,
            delayMillis = DiscountArcProgressConfig.animationDelay,
            easing = 
LinearEasing
        
),
        label = "Arc animation"
    )

    LaunchedEffect(progress.score) {
        //trigger animation to start, must be triggered each time score changes
        //otherwise, arc won't be animated, only the end result will be drawn
        target.floatValue = progress.score.toFloat()
    }
    Column {
        Canvas(
            modifier = Modifier.
aspectRatio
(DiscountArcProgressConfig.aspectRatio)
        ) {
            val centerX = size.width / 2
            val centerY = size.height
            val path = 
Path
().
apply 
{
                arcTo(
                    rect = Rect(
                        centerX - centerY,
                        centerY - centerY,
                        centerX + centerY,
                        centerY + centerY
                    ),
                    startAngleDegrees = DiscountArcProgressConfig.startAngle,
                    //if animate flag is false, we just draw the arc with progress
                    //dashboard case would be 100, full half-circle
                    //if true, we use the animation
                    sweepAngleDegrees = DiscountArcProgressConfig.sweepAngle(
                        if (animate)
                            animation else progress.score.toFloat()
                    ),
                    forceMoveTo = false
                )
            }
            drawPath(
                path = path,
                color = progressColor,
                style = Stroke(
                    width = strokeWidth.
toPx
(),
                    cap = StrokeCap.Round
                )
            )
            if (circularEnd) {
                drawCircle(
                    color = endCircleColor,
                    radius = strokeWidth.
toPx
() / 2,
                    center = DiscountArcProgressConfig.circleOffset(
                        centerX,
                        centerY,
                        animation
                    )
                )
            }
        }
    }
}
Болеслав
Вопрос задан26 апреля 2024 г.

1 Ответ

2
Рубен
Ответ получен3 сентября 2024 г.

Ваш ответ

Загрузить файл.