Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android ListView with paging crashes

Tags:

android

I have pageable ListView and its rows are being loaded asynchronously while scrolling down and sometimes the app just crashes. It's not happening on Android 2.3.3 but does happen on Android 4.0 and above.

Here is my stack trace:

03-07 15:23:02.450: D/AndroidRuntime(1545): Shutting down VM
03-07 15:23:02.450: W/dalvikvm(1545): threadid=1: thread exiting with uncaught exception (group=0x40ccb930)
03-07 15:23:02.455: E/AndroidRuntime(1545): FATAL EXCEPTION: main
03-07 15:23:02.455: E/AndroidRuntime(1545): java.lang.IllegalStateException: The content of the adapter has changed but ListView did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI thread. [in ListView(2131099953, class com.ui.PaginableListView) with Adapter(class android.widget.HeaderViewListAdapter)]
03-07 15:23:02.455: E/AndroidRuntime(1545):         at android.widget.ListView.layoutChildren(ListView.java:1544)
03-07 15:23:02.455: E/AndroidRuntime(1545):         at android.widget.AbsListView$FlingRunnable.run(AbsListView.java:4042)
03-07 15:23:02.455: E/AndroidRuntime(1545):         at android.view.Choreographer$CallbackRecord.run(Choreographer.java:749)
03-07 15:23:02.455: E/AndroidRuntime(1545):         at android.view.Choreographer.doCallbacks(Choreographer.java:562)
03-07 15:23:02.455: E/AndroidRuntime(1545):         at android.view.Choreographer.doFrame(Choreographer.java:531)
03-07 15:23:02.455: E/AndroidRuntime(1545):         at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:735)
03-07 15:23:02.455: E/AndroidRuntime(1545):         at android.os.Handler.handleCallback(Handler.java:725)
03-07 15:23:02.455: E/AndroidRuntime(1545):         at android.os.Handler.dispatchMessage(Handler.java:92)
03-07 15:23:02.455: E/AndroidRuntime(1545):         at android.os.Looper.loop(Looper.java:137)
03-07 15:23:02.455: E/AndroidRuntime(1545):         at android.app.ActivityThread.main(ActivityThread.java:5191)
03-07 15:23:02.455: E/AndroidRuntime(1545):         at java.lang.reflect.Method.invokeNative(Native Method)
03-07 15:23:02.455: E/AndroidRuntime(1545):         at java.lang.reflect.Method.invoke(Method.java:511)
03-07 15:23:02.455: E/AndroidRuntime(1545):         at com.android.internal.os.ZygoteInit$MethodAnd

It's part of the code, probably not very helpful ;/

protected DataResponse doInBackground(IRequest... reqs) {
    IRequest req = reqs[0];
    CancelStateHolder cancelStateHolder = new CancelStateHolder() {
        @Override
        public boolean isCancelled() {
            return DataRequestTask.this.isCancelled();
        }
    };

    if (cacheKey != null) {
        try {
            CacheDogpileProtector.getInstance().acquire(cacheKey);
        } catch (InterruptedException e) {
            cancel(true);
            ZLog.printStackTrace(e);
        }
    }

    reqExec.setParentTask(this);
    DataResponse resp = reqExec.execute(req, cancelStateHolder,
    cacheContext, cacheKey);
    if (resp == null) {
        Log.d(LOG, "resp == null");
    }
    return resp;
}

@Override
protected void onPostExecute(DataResponse result) {
    super.onPostExecute(result);
    ZLog.d(LOG, "override onPostExecute");
    try {
        if (result.getJsonResponse() == null) {
            ZLog.i(LOG, "json is NULL (probably connection problem)   response text: " + result.getResponseText());
            throw new NASAException(R.string.exception_io);
        }
        ConsumptionStatus consumption = ConsumptionStatus.REQUEST_NOT_CONSUMED;
        if (onZadaneRequestCompletedListener != null) {
            consumption = onZadaneRequestCompletedListener.onZadaneRequestCompleted(PaginableListView.this, request, result);
        }

        switch (consumption) {
            case REQUEST_CONSUMED:
                 break;
            case REQUEST_UPDATE_METADATA:
                 wrapAndUpdate(result.getJsonResponse(), true);
                 break;
            case REQUEST_NOT_CONSUMED:
                 wrapAndUpdate(result.getJsonResponse(), false);
                 break;
         }
         if (onZadaneRequestCompletedListener != null) {
             onZadaneRequestCompletedListener.onZadaneRequestCompletedAfterWrap(PaginableListView.this, request, result, lastFetchedPage);
         }
         setOnRefreshListener(properOnRefreshListener);
     } catch (NASAException e) {
         DialogHandler.handleError(mContext, e.getMessage(mContext));
     } finally {
         removeFooterView(footerView);
         actualTask = null;
     }
}

};
actualTask.executeCached(activity, cacheContext, request);
like image 315
Jacek Łyp Avatar asked Mar 07 '13 14:03

Jacek Łyp


1 Answers

You can try adding something like this to your ListView:

    @Override
protected void layoutChildren() {
    try {
        super.layoutChildren();
    } catch (IllegalStateException e) {
        ZLog.e(LOG, "This is not realy dangerous problem");
    }
}

If you add a headerview or a footerview ListView, and when you notifyDataSetChanged(), it will change the mItemCount to the item count of real adapter, but the right side will return the fake itemcount which has added the headercount and footercount:

http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.1.1_r1/android/widget/ListView.java?av=f#1538

like image 88
Roman Barzyczak Avatar answered Nov 08 '22 16:11

Roman Barzyczak