Android 8.0 で java.lang.IndexOutOfBoundsException: setSpan (-1 ... -1) starts before 0
Previewのときからクラッシュレポートが来ていたけど、何が原因かわからず困っていた件が、やっと解決したので超久しぶりに日記に。
問題のエラー。
Fatal Exception: java.lang.IndexOutOfBoundsException: setSpan (-1 ... -1) starts before 0 at android.text.SpannableStringBuilder.checkRange(SpannableStringBuilder.java:1314) at android.text.SpannableStringBuilder.setSpan(SpannableStringBuilder.java:680) at android.text.SpannableStringBuilder.setSpan(SpannableStringBuilder.java:672) at android.view.accessibility.AccessibilityNodeInfo.setText(AccessibilityNodeInfo.java:2474) at android.widget.TextView.onInitializeAccessibilityNodeInfoInternal(TextView.java:10357) at android.view.View.onInitializeAccessibilityNodeInfo(View.java:7307) at android.view.View.createAccessibilityNodeInfoInternal(View.java:7266) at android.view.View.createAccessibilityNodeInfo(View.java:7251) at android.view.accessibility.AccessibilityRecord.setSource(AccessibilityRecord.java:146) at android.view.accessibility.AccessibilityRecord.setSource(AccessibilityRecord.java:119) at android.view.View.onInitializeAccessibilityEventInternal(View.java:7203) at android.widget.TextView.onInitializeAccessibilityEventInternal(TextView.java:10338) at android.view.View.onInitializeAccessibilityEvent(View.java:7191) at android.view.View.sendAccessibilityEventUncheckedInternal(View.java:7053) at android.view.View.sendAccessibilityEventUnchecked(View.java:7038) at android.view.View$SendViewStateChangedAccessibilityEvent.run(View.java:26026) at android.os.Handler.handleCallback(Handler.java:789) at android.os.Handler.dispatchMessage(Handler.java:98) at android.os.Looper.loop(Looper.java:164) at android.app.ActivityThread.main(ActivityThread.java:6541) at java.lang.reflect.Method.invoke(Method.java) at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
エラーが起こっている AccessibilityNodeInfo.java の中身。
public void setText(CharSequence text) { enforceNotSealed(); mOriginalText = text; // Replace any ClickableSpans in mText with placeholders if (text instanceof Spanned) { ClickableSpan[] spans = ((Spanned) text).getSpans(0, text.length(), ClickableSpan.class); if (spans.length > 0) { Spannable spannable = new SpannableStringBuilder(text); for (int i = 0; i < spans.length; i++) { ClickableSpan span = spans[i]; if ((span instanceof AccessibilityClickableSpan) || (span instanceof AccessibilityURLSpan)) { // We've already done enough break; } int spanToReplaceStart = spannable.getSpanStart(span); int spanToReplaceEnd = spannable.getSpanEnd(span); int spanToReplaceFlags = spannable.getSpanFlags(span); spannable.removeSpan(span); ClickableSpan replacementSpan = (span instanceof URLSpan) ? new AccessibilityURLSpan((URLSpan) span) : new AccessibilityClickableSpan(span.getId()); spannable.setSpan(replacementSpan, spanToReplaceStart, spanToReplaceEnd, spanToReplaceFlags); } mText = spannable; return; } } mText = (text == null) ? null : text.subSequence(0, text.length()); }
text の ClickableSpan を AccessibilityClickableSpan に置き換えて返しているのだが、spannable.getSpanStart(span) の span が存在しないので -1 が返ってきてクラッシュしていた。
SpannableString と SpannableStringBuilder のわかりにくい違いとして、SpannableStringBuilder は NoCopySpan がコピーされないという違いがあるけど、これは関係なかった。
もう一つわかりにくい違いがあって、SpannableStringBuilder は setSpan() で長さ 0 の Span を設定しようとすると、設定されずに捨てられるということ。
SpannableStringBuilder#setSpan() で長さ0のClickableSpanを入れてしまっていたために、ここでクラッシュしていた。