Нетривиально (на первый взгляд) StringIndexOutOfBoundsException - PullRequest
2 голосов
/ 22 апреля 2019

Наша система регистрации сбоев показывает сбой, и я не могу понять, как это происходит.Пользовательские данные неизвестны.Я добавил ведение журнала, но результаты будут видны только после следующего выпуска, ~ 2 недели.

Как приведенный ниже код может вызвать следующее исключение:

Crashes with java.lang.StringIndexOutOfBoundsException length=0; index=1

Не из-запроблема параллелизма?Другими словами, в какой ситуации может произойти сбой и как его избежать (не включая try-catch)?Что может быть в случае, если это проблема параллелизма (я не вижу ее, но, возможно, я ошибаюсь)?

private String contactName; // has getter and setter

public String getFirstSymbol() {
  String firstSymbol = "";
  String trimmed = contactName.trim();
  if (!TextUtils.isEmpty(trimmed)) { // TextUtils.isEmpty is Android method: return str == null || str.length() == 0;
    String[] parts = trimmed.toUpperCase().split("\\s+");
    firstSymbol = parts[0].substring(0, 1); // crashes here:
  }
  return firstSymbol;
 }

На прошлой неделе на 7 устройствах произошло ~ 100 сбоев.

Вот трассировка стека

  Fatal Exception: java.lang.StringIndexOutOfBoundsException: length=0; index=1
  at java.lang.String.substring(String.java:1971)
  at my.app.model.Contact.getFirstSymbol(Contact.java:256)
  at my.app.ui.AvatarView.bind(AvatarView.java:73)
  at my.app.viewholder.CallLogViewHolder.bind(CallLogViewHolder.java:50)
  at my.app.viewholder.CallLogViewHolder.bind(CallLogViewHolder.java:23)
  at my.app.ContactListAdapter.onBindViewHolder(ContactListAdapter.java:68)
  at my.app.ContactListAdapter.onBindViewHolder(ContactListAdapter.java:25)
  at androidx.recyclerview.widget.RecyclerView$Adapter.onCreateViewHolder(RecyclerView.java:6781)
  at androidx.recyclerview.widget.RecyclerView$Adapter.onBindViewHolder(RecyclerView.java:6781)
  at androidx.recyclerview.widget.RecyclerView$Adapter.getItemCount(RecyclerView.java:6781)
  at androidx.recyclerview.widget.RecyclerView$Adapter.bindViewHolder(RecyclerView.java:6823)
  at androidx.recyclerview.widget.RecyclerView$Recycler.tryBindViewHolderByDeadline(RecyclerView.java:5752)
  at androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:6019)
  at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5858)
  at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5854)
  at androidx.recyclerview.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2230)
  at androidx.recyclerview.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1557)
  at androidx.recyclerview.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1517)
  at androidx.recyclerview.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:612)
  at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:3924)
  at androidx.recyclerview.widget.RecyclerView.dispatchLayout(RecyclerView.java:3641)
  at androidx.recyclerview.widget.RecyclerView.onLayout(RecyclerView.java:4194)
  at android.view.View.layout(View.java:20964)
  at android.view.ViewGroup.layout(ViewGroup.java:6440)
  at android.widget.FrameLayout.layoutChildren(FrameLayout.java:323)
  at android.widget.FrameLayout.onLayout(FrameLayout.java:261)
  at android.view.View.layout(View.java:20964)
  at android.view.ViewGroup.layout(ViewGroup.java:6440)
  at androidx.viewpager.widget.ViewPager.onLayout(ViewPager.java:1775)
  at android.view.View.layout(View.java:20964)
  at android.view.ViewGroup.layout(ViewGroup.java:6440)
  at com.google.android.material.appbar.HeaderScrollingViewBehavior.layoutChild(HeaderScrollingViewBehavior.java:142)
  at com.google.android.material.appbar.HeaderScrollingViewBehavior.findFirstDependency(HeaderScrollingViewBehavior.java:142)
  at com.google.android.material.appbar.ViewOffsetBehavior.onLayoutChild(ViewOffsetBehavior.java:41)
  at com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior.onMeasureChild(AppBarLayout.java:1556)
  at androidx.coordinatorlayout.widget.CoordinatorLayout.onLayout(CoordinatorLayout.java:888)
  at android.view.View.layout(View.java:20964)
  at android.view.ViewGroup.layout(ViewGroup.java:6440)
  at android.widget.FrameLayout.layoutChildren(FrameLayout.java:323)
  at android.widget.FrameLayout.onLayout(FrameLayout.java:261)
  at android.view.View.layout(View.java:20964)
  at android.view.ViewGroup.layout(ViewGroup.java:6440)
  at com.google.android.material.appbar.HeaderScrollingViewBehavior.layoutChild(HeaderScrollingViewBehavior.java:142)
  at com.google.android.material.appbar.HeaderScrollingViewBehavior.findFirstDependency(HeaderScrollingViewBehavior.java:142)
  at com.google.android.material.appbar.ViewOffsetBehavior.onLayoutChild(ViewOffsetBehavior.java:41)
  at com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior.onMeasureChild(AppBarLayout.java:1556)
  at androidx.coordinatorlayout.widget.CoordinatorLayout.onLayout(CoordinatorLayout.java:888)
  at android.view.View.layout(View.java:20964)
  at android.view.ViewGroup.layout(ViewGroup.java:6440)
  at android.widget.FrameLayout.layoutChildren(FrameLayout.java:323)
  at android.widget.FrameLayout.onLayout(FrameLayout.java:261)
  at android.view.View.layout(View.java:20964)
  at android.view.ViewGroup.layout(ViewGroup.java:6440)
  at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1791)
  at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1635)
  at android.widget.LinearLayout.onLayout(LinearLayout.java:1544)
  at android.view.View.layout(View.java:20964)
  at android.view.ViewGroup.layout(ViewGroup.java:6440)
  at android.widget.FrameLayout.layoutChildren(FrameLayout.java:323)
  at android.widget.FrameLayout.onLayout(FrameLayout.java:261)
  at android.view.View.layout(View.java:20964)
  at android.view.ViewGroup.layout(ViewGroup.java:6440)
  at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1791)
  at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1635)
  at android.widget.LinearLayout.onLayout(LinearLayout.java:1544)
  at android.view.View.layout(View.java:20964)
  at android.view.ViewGroup.layout(ViewGroup.java:6440)
  at android.widget.FrameLayout.layoutChildren(FrameLayout.java:323)
  at android.widget.FrameLayout.onLayout(FrameLayout.java:261)
  at com.android.internal.policy.DecorView.onLayout(DecorView.java:955)
  at android.view.View.layout(View.java:20964)
  at android.view.ViewGroup.layout(ViewGroup.java:6440)
  at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:3092)
  at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2779)
  at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1863)
  at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:8072)
  at android.view.Choreographer$CallbackRecord.run(Choreographer.java:911)
  at android.view.Choreographer.doCallbacks(Choreographer.java:723)
  at android.view.Choreographer.doFrame(Choreographer.java:658)
  at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:897)
  at android.os.Handler.handleCallback(Handler.java:790)
  at android.os.Handler.dispatchMessage(Handler.java:99)
  at android.os.Looper.loop(Looper.java:164)
  at android.app.ActivityThread.main(ActivityThread.java:7000)
  at java.lang.reflect.Method.invoke(Method.java)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:441)
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1408)

1 Ответ

2 голосов
/ 22 апреля 2019

Здесь нет проблем с безопасностью потоков / параллелизмом.Единственное значение, которое этот метод потенциально разделяет с другими потоками, это значение contactName.Это ссылка String.String является неизменным.Другие значения, которые этот метод извлекает из (потенциально) общего неизменяемого String, все ограничены потоками.

Кроме того, я сбит с толку.trim должен удалить любой начальный или конечный символ, который будет соответствовать \s+.И защита !isEmpty исключает возможность пустой строки.Таким образом, массив parts должен содержать хотя бы 1 элемент, а первый элемент должен содержать хотя бы один символ.

Но исключения, по-видимому, говорят иначе.

Таким образом, это оставляет «альтернативные» объяснения, которые обычно игнорируют как нелепые:

  • Может быть, это происходит на платформе со сломанным JIT-компилятором.
  • Возможно, это происходит на платформе с ошибкой в ​​String или Pattern.
  • Может быть, это аппаратная проблема.
  • Возможно, существует версия вашего приложения, чьяскомпилированный код не соответствует исходному коду, который вы просматриваете.

Ни одно из этих "альтернативных" объяснений не дает вам никакого пути вперед.Итак, если вы не найдете лучшего объяснения, я предлагаю вам окружить этот код, вызывающий проблемы, try { ... } catch и попытаться записать длину и содержимое строки contactName, которые вызывают проблему.Включите это в следующую версию, и если / когда вы начнете получать сбои, будьте готовы выпустить небольшую версию с реальным исправлением.Или просто обработайте это так, как если бы пользователь ввел пустое имя контакта ... или что-то в этом роде.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...