ListView random IndexOutOfBoundsException on Froyo

I have an app with tons of downloads and I'm receiving a lot of this error:

 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)

As you can see in the stack trace, there is not one line with my code trace in it. To reproduce this, I luckily found an user with a Froyo (2.2 p7) and I just scrolled down one of the ListViews on the code. After some random time, it just froze with this exception. This happens every time at a different time.

It is a ListView with an EndlessAdapter behind it, just adding more and more rows. Apparently I get this error when I do the "over-scroll" but I cannot even think of a way to fix this. It is hard enough to reproduce, but with almost 200 users online at every time, if they keep getting this error they will end up not using the app anymore.

Any help would be appreciated.

EDIT: There has been a similar issue with someone else with the EndlessAdapter. http://groups.google.com/group/cw-android/browse_thread/thread/4739ce05742841da/af59c779e99f5e23?lnk=gst&q=index#af59c779e99f5e23

But it's not EndlessAdapter's fault. It's android's fault.

Answers


After a lot of time checking the android source code and not understanding this error I've finally cracked it.

The problem occurs with Samsung phones, they have a different implementation on the over-scroll functionality and that ended up throwing this exception as it tried to select a footer/header out of bounds (even when there is no footer view).

The solution I used is not pretty but it will stop this error from happening ever again.

class MyFixedListView extends ListView {
    @Override
    protected void dispatchDraw(Canvas canvas) {
        try {
            super.dispatchDraw(canvas);
        } catch (IndexOutOfBoundsException e) {
            // samsung error
        }
    }
}

Now I use this ListView implementation and the error is gone.

I really hope this helps anyone using endless adapters.


Same issue happens in my app when testing on the ICS tablets. It happens only when I have 11 elements in the list, when EndlessAdapter loads by 10 items at time.

This is a race condition issue. The problem comes from the keepOnAppending flag update on the AsyncTask thread. While ListView accesses getCount on the UI thread andkeepOnAppending is set to true, later when it calls getView in the same UI thread andkeepOnAppending is already reset in the 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();
    }
}

As a fix, I changed the code of the AppendTask so that it updates keepOnAppending on the UI thread, only when new items added to the adapter.

Here is the code:

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

Need Your Help

Android: how to check if a View inside of ScrollView is visible?

android scrollview visible

I have a ScrollView which holds a series of Views. I would like to be able to determine if a view is currently visible (if any part of it is currently displayed by the ScrollView). I would expect ...

Convert DataRowCollection to DataRow[]

c# performance datarow datarowcollection

What's the best performing way to convert a DataRowCollection instance to a DataRow[]?