启用辅助功能与 Jetpack Compose 和 AndroidView 的结合导致 onInitializeAccessibilityNodeInfo 中的 NullPointerException

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

目前我在 FirebaseCrashlytics 中看到很多

NullpointerExceptions
。 NPE 出现在 AndroidComposeView.kt 的这一行中。问题可能是TalkBack 结合
AndroidView
引起的,但我无法在本地重现。这是一个已知的问题,没有任何解决方法: 这是堆栈跟踪:

androidx.compose.ui.platform.AndroidComposeView$addAndroidView$1.onInitializeAccessibilityNodeInfo (AndroidComposeView.android.kt:712)
androidx.core.view.AccessibilityDelegateCompat$AccessibilityDelegateAdapter.onInitializeAccessibilityNodeInfo (AccessibilityDelegateCompat.java:91)
android.view.View.onInitializeAccessibilityNodeInfo (View.java:9095)
android.view.View.createAccessibilityNodeInfoInternal (View.java:9056)
android.view.View$AccessibilityDelegate.createAccessibilityNodeInfo (View.java:32397)
android.view.View.createAccessibilityNodeInfo (View.java:9039)
android.view.AccessibilityInteractionController.populateAccessibilityNodeInfoForView (AccessibilityInteractionController.java:440)
android.view.AccessibilityInteractionController.findAccessibilityNodeInfoByAccessibilityIdUiThread (AccessibilityInteractionController.java:383)
android.view.AccessibilityInteractionController.-$$Nest$mfindAccessibilityNodeInfoByAccessibilityIdUiThread (AccessibilityInteractionController.java)
android.view.AccessibilityInteractionController$PrivateHandler.handleMessage (AccessibilityInteractionController.java:1713)
android.os.Handler.dispatchMessage (Handler.java:106)
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)
com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:571)
com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1067) 

是否有解决方法,或者更好的方法来避免此异常?

android nullpointerexception android-jetpack-compose
1个回答
0
投票

现在,我们找到了解决方法。我们仍然使用 FirebaseCrashlytics 跟踪异常作为非致命异常,我们看到它有效。这是解决方法

CrashFixedAndroidView.kt

/**
 * Workaround to fix NullPointerException in AndroidComposeView$addAndroidView$1.onInitializeAccessibilityNodeInfo().
 * It can be used like the default AndroidView.
 */
@Composable
fun <T : View> CrashFixedAndroidView(
    factory: (Context) -> T,
    modifier: Modifier = Modifier,
    update: (T) -> Unit = NoOpUpdate
) {
    val crashFix = remember { AccessibilityCrashFix() }
    AndroidView(
        factory = factory,
        modifier = modifier.onPlaced { crashFix.applyFix() },
        update = { view ->
            crashFix.updateView(view)
            update(view)
        }
    )
}

/**
 * Gets the existing AccessibilityDelegate and wraps it in another AccessibilityDelegate to surround its call with a try catch.
 */
private class AccessibilityCrashFix {
    private var androidViewReference: WeakReference<View>? = null
    private var isFixApplied = false

    fun updateView(view: View) {
        if (androidViewReference != null) {
            return
        }
        val androidView = view.parent as? View ?: return
        androidViewReference = WeakReference(androidView)
    }

    fun applyFix() {
        if (isFixApplied) {
            // Prevents multiple wrapping
            return
        }
        val androidView = androidViewReference?.get() ?: return
        val crashDelegate = ViewCompat.getAccessibilityDelegate(androidView)
        if (crashDelegate != null) {
            val catchedDelegate = catchedDelegate(crashDelegate)
            ViewCompat.setAccessibilityDelegate(androidView, catchedDelegate)
        }
        isFixApplied = true
    }

    private fun catchedDelegate(crashDelegate: AccessibilityDelegateCompat): AccessibilityDelegateCompat {
        return object : AccessibilityDelegateCompat() {
            override fun onInitializeAccessibilityNodeInfo(
                host: View,
                info: AccessibilityNodeInfoCompat
            ) {
                try {
                    crashDelegate.onInitializeAccessibilityNodeInfo(host, info)
                } catch (e: Exception) {
                    Log.e(Tags.TAG_SYS, "Catched accessibility crash", e)
                    FirebaseCrashlytics.getInstance().recordException(CatchedAccessibilityException(e))
                }
            }
        }
    }

    class CatchedAccessibilityException(cause: Throwable): Exception("Catched crash", cause)
}
© www.soinside.com 2019 - 2024. All rights reserved.