Play商店预发布报告中的Android应用程序崩溃但在真实设备中工作

Posted

技术标签:

【中文标题】Play商店预发布报告中的Android应用程序崩溃但在真实设备中工作【英文标题】:Android app crash in play store prelaunch report but working in real device 【发布时间】:2019-07-13 17:18:48 【问题描述】:

无法跟踪项目中的崩溃,我在 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)

【问题讨论】:

*setSpan (4 ... 4) ends beyond length 0 在调用 setSpan 方法之前检查文本长度。 我没有使用setSpan方法,只使用edittext的方法是""" editText.isFocused(); editText.requestFocus(); editText.setText(""); """ 我的一个应用程序中确实存在这个问题,已经好几个月了。仅显示在发布前报告中,从不显示在生产中。而且我也不使用 setSpan。 这里也一样。对我来说,这也是由performAccessibilityAction 引起的。对我来说,这看起来像是 SDK 中的错误 我看到了完全相同的问题,只是它发生在运行 Android 6.0.1 的(真正的)根设备上。它从未被修复过。 【参考方案1】:

我在发布前报告和 FireBase 测试实验室中都看到了他的相同错误。我花了几个小时研究这个问题,我确信这是一个只影响 SDK

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

 Bundle arguments = new Bundle();
 arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, "1234");
 mEditText.performAccessibilityAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments);

那么,该怎么办呢?

一种选择是忽略它,因为它可能会影响一小部分用户。除非您认为您可能有许多用户使用辅助功能输入文本,并且他们也使用旧版 SDK (

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

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

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;
                    
                );

            
        

    

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

基本上,如果 SDK

【讨论】:

+1 建议如何重现!为了将来参考,有时您甚至需要更长的字符串才能崩溃,因为“1234”可能不够长【参考方案2】:

受@smitty1 的solution 启发,我做了这个。我在自定义 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)
    

【讨论】:

【参考方案3】:

在我的情况下,EditText 没有设置maxLength。但是我们使用了一些自定义的InputFilter。如下所示:

private val textFilter = object : InputFilter 

    override fun filter(source: CharSequence, start: Int, end: Int, dest: Spanned, dstart: Int, dend: Int): CharSequence? 
        return when 
            good -> null // just allow change as is
            bad -> "" // ignore change totally[*].
            else -> magic // return a calculated value
        
    

问题[*] 通过返回空字符串完全忽略了该值,这导致 Marshmellow 及以下设备崩溃。这是我解决这个问题的方法:

private val textFilter = object : InputFilter 

    override fun filter(source: CharSequence, start: Int, end: Int, dest: Spanned, dstart: Int, dend: Int): CharSequence? 
        return when 
            good -> null // just allow change as is
            bad -> (dest.toString() + source.subSequence(start, end)).subSequence(dstart, dstart + (end - start)) // ignore change totally[*].
            else -> magic // return a calculated value
        
    

关键点是不返回任何小于更改(end-start) (在M及以下设备上)


我仍然感谢以前的答案,它们帮助我确定了根本原因。

【讨论】:

以上是关于Play商店预发布报告中的Android应用程序崩溃但在真实设备中工作的主要内容,如果未能解决你的问题,请参考以下文章

Android Play 商店中的 Delphi 11 应用程序显示警告:非 SDK API

如何仅针对已安装应用程序的用户在 Android Play 商店上发布应用程序?

gsutil - 如何从 google play 获取 Android 应用审查报告

Google Play控制台预发布报告 - 内存

从 MI 设备上的 Play 商店获取“中止异常”报告

Google Play商店为预注册的游戏和应用提供自动安装功能