Play 商店预发布报告中的 Android 应用程序崩溃,但可在真实设备中运行

Rav*_*dav 18 android android-edittext indexoutofboundsexception

无法跟踪项目中的崩溃,我在 Play 商店预发布部分遇到了这个错误,它在点击 时显示EditText,它得到了错误。但在真实设备上没有发生任何崩溃。

问题:java.lang.IndexOutOfBoundsException:setSpan (4 ... 4) 结束超过长度 0

Fatal Exception: java.lang.IndexOutOfBoundsException: setSpan (4 ... 4) ends beyond length 0
       at android.text.SpannableStringBuilder.checkRange(SpannableStringBuilder.java:1096)
       at android.text.SpannableStringBuilder.setSpan(SpannableStringBuilder.java:671)
       at android.text.SpannableStringBuilder.setSpan(SpannableStringBuilder.java:664)
       at android.text.Selection.setSelection(Selection.java:76)
       at android.text.Selection.setSelection(Selection.java:87)
       at android.widget.EditText.setSelection(EditText.java:98)
       at android.widget.EditText.performAccessibilityActionInternal(EditText.java:138)
       at android.view.View.performAccessibilityAction(View.java:8892)
       at android.view.AccessibilityInteractionController.performAccessibilityActionUiThread(AccessibilityInteractionController.java:668)
       at android.view.AccessibilityInteractionController.-wrap6(AccessibilityInteractionController.java)
       at android.view.AccessibilityInteractionController$PrivateHandler.handleMessage(AccessibilityInteractionController.java:1194)
       at android.os.Handler.dispatchMessage(Handler.java:102)
       at android.os.Looper.loop(Looper.java:148)
       at android.app.ActivityThread.main(ActivityThread.java:5459)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:728)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
Run Code Online (Sandbox Code Playgroud)

smi*_*ty1 13

我在发布前报告和 FireBase 测试实验室中都看到了他同样的错误。我花了几个小时研究这个问题,我确定这是一个只影响 SDK <= 23 的错误,并且由 AccessibilityNodeInfo#performAction(ACTION_SET_TEXT) 触发。似乎这个方法完全忽略了 maxLength 属性,这反过来导致了 IndexOutOfBoundsException。

您可以使用以下代码复制相同的异常,确保字符串“1234”比您的 maxLength 长:

 Bundle arguments = new Bundle();
 arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, "1234");
 mEditText.performAccessibilityAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments);
Run Code Online (Sandbox Code Playgroud)

那么,该怎么办呢?

一种选择是忽略它,因为它可能会影响非常小的用户子集。除非您认为您可能有许多用户使用辅助功能来输入文本,并且他们也在使用较旧的 SDK (<= 23)。

另一种选择是将 build.gradle 中的 minSDKVersion 设置为 24。如果您有很多用户使用 SDK <= 23,这可能会造成伤害

第三种选择是使用我想出的这个非常丑陋的解决方法:

if(Build.VERSION.SDK_INT <= 23){
        ViewGroup rootView = findViewById(R.id.your_layout_that_contains_edittexts);
        ArrayList<View> views = rootView.getTouchables();
        for(View view : views){
            if(view instanceof EditText){
                EditText mEditText = (EditText)view;
                mEditText.setAccessibilityDelegate(new View.AccessibilityDelegate() {
                    @Override
                    public boolean performAccessibilityAction(View host, int action, Bundle arguments) {

                        if (action == AccessibilityNodeInfo.ACTION_SET_TEXT) {
                            //do something here to make sure index out of bounds does not occur
                            int maxLength = 0;
                            for(InputFilter filter : mEditText.getFilters()){
                                if(filter instanceof InputFilter.LengthFilter)  {
                                    maxLength = ((InputFilter.LengthFilter)filter).getMax();
                                }
                            }

                            Set<String> keys  = arguments.keySet();
                            for(String key : keys){
                                if(arguments.get(key) instanceof  CharSequence){
                                    if(arguments.get(key) != null) {
                                        arguments.putCharSequence(key, ((CharSequence) arguments.get(key)).subSequence(0, maxLength));
                                        mEditText.setText(arguments.getCharSequence(key));
                                    }
                                }
                            }
                        }
                        return true;
                    }
                });

            }
        }

    }
Run Code Online (Sandbox Code Playgroud)

好的,所以我确实说它很丑,但它确实有效。将 your_layout_that_contains_edittexts 替换为包含所有 EditText 的布局的 id。

基本上,如果 SDK <= 23,它会循环遍历 ViewGroup 中的所有 EditText,对于每个 EditText,通过 AccessibilityDelegate 覆盖 performAccessibilityAction 并确保输入的文本永远不会超过 EditText 的 maxLength 值。

  • +1关于如何重现的建议!为了将来的参考,有时你甚至需要更长的字符串来崩溃,因为“1234”可能不够长 (3认同)

And*_*æss 6

受到@smitty1解决方案的启发,我做了这个。我在自定义 EditText 中处理它,而不是在视图中检查所有 EditText:

open class MaxLengthEditText : AppCompatAutoCompleteTextView {

    constructor(context: Context) : super(context)
    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)

    /*
     * On Android API versions <= 23 the app crashes if a text that is longer than the max length of the EditText
     * is set using an accessibility action. To avoid this we cut the text down to the maximum allowed length.
     */
    override fun performAccessibilityAction(action: Int, arguments: Bundle?): Boolean {
        if (Build.VERSION.SDK_INT <= 23 && action == AccessibilityNodeInfo.ACTION_SET_TEXT) {
            filters.forEach { filter ->
                if (filter is InputFilter.LengthFilter) {
                    val maxLength = filter.max
                    arguments?.keySet()?.forEach { key ->
                        if (arguments[key] is CharSequence) {
                            val shorterText = 
arguments.getCharSequence(key)?.subSequence(0, maxLength)
                            setText(shorterText)
                            return true
                        }
                    }
                }
            }
        }

        return super.performAccessibilityAction(action, arguments)
    }
}
Run Code Online (Sandbox Code Playgroud)