Fire TV点击软键盘清除按钮时Jetpack Compose Outlined TextField崩溃,安卓TV正常
在Fire TV设备上使用Jetpack Compose开发的OutlinedTextField,输入内容后点击软键盘的清除按钮会触发崩溃,但相同功能在Android TV上完全正常。
崩溃日志
FATAL EXCEPTION: main
Process: com.abc.firetv.screen.debug, PID: 3070
java.lang.RuntimeException: Error while applying EditCommand batch to buffer (length=1, composition=null, selection=TextRange(1, 1)):
CommitTextCommand(text.length=0, newCursorPosition=0)DeleteSurroundingTextCommand(lengthBeforeCursor=2147483647, lengthAfterCursor=2147483647)
at androidx.compose.ui.text.input.EditProcessor.apply(EditProcessor.kt:117)
at androidx.compose.foundation.text.TextFieldDelegate$Companion.onEditCommand$foundation_release(TextFieldDelegate.kt:194)
at androidx.compose.foundation.text.TextFieldDelegate$Companion$restartInput$1.invoke(TextFieldDelegate.kt:254)
at androidx.compose.foundation.text.TextFieldDelegate$Companion$restartInput$1.invoke(TextFieldDelegate.kt:251)
at androidx.compose.ui.text.input.TextInputServiceAndroid$createInputConnection$1.onEditCommands(TextInputServiceAndroid.android.kt:128)
at androidx.compose.ui.text.input.RecordingInputConnection.endBatchEditInternal(RecordingInputConnection.android.kt:157)
at androidx.compose.ui.text.input.RecordingInputConnection.endBatchEdit(RecordingInputConnection.android.kt:151)
at com.android.internal.view.IInputConnectionWrapper.executeMessage(IInputConnectionWrapper.java:486)
at com.android.internal.view.IInputConnectionWrapper$MyHandler.handleMessage(IInputConnectionWrapper.java:93)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6200)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:911)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:801)
Caused by: java.lang.IllegalArgumentException: end cannot be negative. [start: 1, end: -2147483648]
at androidx.compose.ui.text.TextRangeKt.packWithCheck(TextRange.kt:124)
at androidx.compose.ui.text.TextRangeKt.TextRange(TextRange.kt:37)
at androidx.compose.ui.text.input.EditingBuffer.delete$ui_text_release(EditingBuffer.kt:211)
at androidx.compose.ui.text.input.DeleteSurroundingTextCommand.applyTo(EditCommand.kt:266)
at androidx.compose.ui.text.input.EditProcessor.apply(EditProcessor.kt:114)
... 15 more
2023-03-24 23:48:45.317 3070-3070 Process com.abc.firetv.screen.debug I Sending signal. PID: 3070 SIG: 9
2023-03-24 23:48:45.428 613-656 InputDispatcher pid-613 E channel 'a814ebc com.abc.firetv.screen.debug/tv.abc.mobile.presentation.onboarding.dispatch.controller.DispatchActivity (server)' ~ Channel is unrecoverably broken and will be disposed!
问题原因
Fire TV的软键盘发送了DeleteSurroundingTextCommand命令,其中lengthBeforeCursor和lengthAfterCursor的值为2147483647(即Integer.MAX_VALUE)。Compose的EditingBuffer计算删除范围时,用当前光标位置减去这个过大数值,导致算出的end为负数,触发IllegalArgumentException。而Android TV输入法发送的参数在正常范围内,因此不会崩溃。
解决方案
方案1:升级Jetpack Compose版本
该问题在Jetpack Compose 1.4.0及以上稳定版本中已被官方修复,建议将项目中compose-ui、compose-foundation等Compose相关依赖升级至最新稳定版。
方案2:自定义InputConnection拦截异常命令
若暂时无法升级Compose版本,可通过自定义InputConnection拦截异常删除命令,修正参数后再传递给默认实现:
@Composable fun FixedOutlinedTextField( value: String, onValueChange: (String) -> Unit, modifier: Modifier = Modifier, label: @Composable (() -> Unit)? = null, // 根据需求添加其他TextField参数 ) { val interactionSource = remember { MutableInteractionSource() } val textInputService = LocalTextInputService.current OutlinedTextField( value = value, onValueChange = onValueChange, modifier = modifier, label = label, interactionSource = interactionSource, inputTransformation = remember(textInputService) { object : TextInputTransformation { override fun transformInput(input: TextFieldValue, service: TextInputService): TextFieldValue { return input } override fun createInputConnection(outAttrs: EditorInfo, service: TextInputService): InputConnection { val defaultConnection = service.createInputConnection(outAttrs) return object : InputConnectionWrapper(defaultConnection, true) { override fun deleteSurroundingText(beforeLength: Int, afterLength: Int): Boolean { // 将过大的长度值限制在合理范围内,避免计算负数 val safeBefore = beforeLength.coerceAtMost(value.length) val safeAfter = afterLength.coerceAtMost(value.length - value.length) return super.deleteSurroundingText(safeBefore, safeAfter) } } } } } ) }
使用该自定义FixedOutlinedTextField替代原有OutlinedTextField,即可规避Fire TV上的崩溃问题。
内容的提问来源于stack exchange,提问作者Rishabh Ritweek

