ListView random IndexOutOfBoundsException на Froyo
У меня есть приложение с множеством загрузок, и я получаю много этой ошибки:
16783 AndroidRuntime E java.lang.IndexOutOfBoundsException: Invalid index 0, size is 0
16783 AndroidRuntime E at java.util.ArrayList.throwIndexOutOfBoundsException(ArrayList.java:257)
16783 AndroidRuntime E at java.util.ArrayList.get(ArrayList.java:311)
16783 AndroidRuntime E at android.widget.HeaderViewListAdapter.isEnabled(HeaderViewListAdapter.java:16
4)
16783 AndroidRuntime E at android.widget.ListView.dispatchDrawWithExcessScroll_Default(ListView.java:3
288)
16783 AndroidRuntime E at android.widget.ListView.dispatchDraw(ListView.java:3029)
16783 AndroidRuntime E at android.view.View.draw(View.java:6743)
16783 AndroidRuntime E at android.widget.AbsListView.draw(AbsListView.java:2549)
16783 AndroidRuntime E at android.view.ViewGroup.drawChild(ViewGroup.java:1640)
16783 AndroidRuntime E at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1367)
16783 AndroidRuntime E at android.view.View.draw(View.java:6743)
16783 AndroidRuntime E at android.view.ViewGroup.drawChild(ViewGroup.java:1640)
16783 AndroidRuntime E at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1367)
16783 AndroidRuntime E at android.view.ViewGroup.drawChild(ViewGroup.java:1638)
16783 AndroidRuntime E at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1367)
16783 AndroidRuntime E at android.view.ViewGroup.drawChild(ViewGroup.java:1638)
16783 AndroidRuntime E at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1367)
16783 AndroidRuntime E at android.view.ViewGroup.drawChild(ViewGroup.java:1638)
16783 AndroidRuntime E at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1367)
16783 AndroidRuntime E at android.view.ViewGroup.drawChild(ViewGroup.java:1638)
16783 AndroidRuntime E at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1367)
16783 AndroidRuntime E at android.view.ViewGroup.drawChild(ViewGroup.java:1638)
16783 AndroidRuntime E at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1367)
16783 AndroidRuntime E at android.view.ViewGroup.drawChild(ViewGroup.java:1638)
16783 AndroidRuntime E at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1367)
16783 AndroidRuntime E at android.view.ViewGroup.drawChild(ViewGroup.java:1638)
16783 AndroidRuntime E at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1367)
16783 AndroidRuntime E at android.view.View.draw(View.java:6743)
16783 AndroidRuntime E at android.view.ViewGroup.drawChild(ViewGroup.java:1640)
16783 AndroidRuntime E at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1367)
16783 AndroidRuntime E at android.view.ViewGroup.drawChild(ViewGroup.java:1638)
16783 AndroidRuntime E at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1367)
16783 AndroidRuntime E at android.view.View.draw(View.java:6743)
16783 AndroidRuntime E at android.widget.FrameLayout.draw(FrameLayout.java:352)
16783 AndroidRuntime E at com.android.internal.policy.impl.PhoneWindow$DecorView.draw(PhoneWindow.java
:1885)
16783 AndroidRuntime E at android.view.ViewRoot.draw(ViewRoot.java:1407)
16783 AndroidRuntime E at android.view.ViewRoot.performTraversals(ViewRoot.java:1163)
16783 AndroidRuntime E at android.view.ViewRoot.handleMessage(ViewRoot.java:1727)
16783 AndroidRuntime E at android.os.Handler.dispatchMessage(Handler.java:99)
16783 AndroidRuntime E at android.os.Looper.loop(Looper.java:123)
16783 AndroidRuntime E at android.app.ActivityThread.main(ActivityThread.java:4627)
16783 AndroidRuntime E at java.lang.reflect.Method.invokeNative(Native Method)
16783 AndroidRuntime E at java.lang.reflect.Method.invoke(Method.java:521)
16783 AndroidRuntime E at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:8
58)
16783 AndroidRuntime E at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
16783 AndroidRuntime E at dalvik.system.NativeStart.main(Native Method)
Как вы можете видеть в трассировке стека, в моей кодовой трассе нет одной строки.
Чтобы воспроизвести это, я, к счастью, нашел пользователя с Froyo (2.2 p7), и я просто прокрутил один из ListView
кода. После некоторого случайного времени он просто застыл с этим исключением. Это происходит каждый раз в другое время.
Это ListView
с EndlessAdapter
позади него, просто добавляя все больше и больше строк.
По-видимому, я получаю эту ошибку, когда выполняю "сверхпрокрутку", но я даже не могу придумать, как это исправить.
Достаточно сложно воспроизвести, но с почти 200 пользователями онлайн каждый раз, если они продолжают получать эту ошибку, они больше не будут использовать приложение.
Любая помощь будет оценена.
EDIT:
Аналогичная проблема возникла с кем-то еще с EndlessAdapter
. http://groups.google.com/group/cw-android/browse_thread/thread/4739ce05742841da/af59c779e99f5e23?lnk=gst&q=index#af59c779e99f5e23
Но это не ошибка EndlessAdapter
. Это андроидная неисправность.
Ответы
Ответ 1
После много времени проверяя исходный код Android и не понимая эту ошибку, я, наконец, взломал ее.
Проблема возникает с телефонами Samsung, у них есть другая реализация над функциями прокрутки, и это закончилось тем, что выбрасывало это исключение, поскольку оно пыталось выбрать нижний колонтитул/градиент вне пределов (даже если нет нижнего колонтитула).
Решение, которое я использовал, не очень красивое, но это снова остановит эту ошибку.
class MyFixedListView extends ListView {
@Override
protected void dispatchDraw(Canvas canvas) {
try {
super.dispatchDraw(canvas);
} catch (IndexOutOfBoundsException e) {
// samsung error
}
}
}
Теперь я использую эту реализацию ListView
, и ошибка исчезла.
Я действительно надеюсь, что это поможет любому, кто использует бесконечные адаптеры.
Ответ 2
Такая же проблема возникает в моем приложении при тестировании на ICS-планшетах. Это происходит только тогда, когда у меня есть 11 элементов в списке, когда EndlessAdapter загружается на 10 элементов в момент времени.
Это проблема гонки. Проблема связана с обновлением флага keepOnAppending в потоке AsyncTask. В то время как ListView обращается к getCount в потоке пользовательского интерфейса andkeepOnAppending имеет значение true, позже, когда он вызывает getView в одном и том же потоке пользовательского интерфейса, andkeepOnAppending уже reset в doInBackground:
@Override
public int getCount() {
if (keepOnAppending.get()) {
return (super.getCount() + 1); // one more for
// "pending"
}
return (super.getCount());
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (position == super.getCount() && keepOnAppending.get()) {
if (pendingView == null) {
pendingView = getPendingView(parent);
executeAsyncTask(new AppendTask());
}
return (pendingView);
}
return (super.getView(position, convertView, parent));
}
class AppendTask extends AsyncTask<Void, Void, Exception> {
@Override
protected Exception doInBackground(Void... params) {
Exception result = null;
try {
keepOnAppending.set(cacheInBackground());
} catch (Exception e) {
result = e;
}
return (result);
}
@Override
protected void onPostExecute(Exception e) {
if (e == null) {
appendCachedData();
} else {
keepOnAppending.set(onException(pendingView, e));
}
pendingView = null;
notifyDataSetChanged();
}
}
В качестве исправления я изменил код AppendTask так, чтобы он обновлял keepOnAppending в потоке пользовательского интерфейса, только когда новые элементы добавлены в адаптер.
Вот код:
class AppendTask extends AsyncTask<Void, Void, AppendTask.Result> {
class Result {
boolean status;
Exception ex;
public Result(Exception ex) {
this.ex = ex;
}
public Result(boolean result) {
this.status = result;
}
}
@Override
protected AppendTask.Result doInBackground(Void... params) {
try {
return new Result(cacheInBackground());
} catch (Exception e) {
return new Result(e);
}
}
@Override
protected void onPostExecute(AppendTask.Result result) {
if (result.ex == null) {
appendCachedData();
keepOnAppending.set(result.status);
} else {
keepOnAppending.set(onException(pendingView, result.ex));
}
pendingView = null;
notifyDataSetChanged();
}
}