Question

I am seeing some strange behaviour with onPause / onResume in my app and cannot work out what is happening.

I perform a database query (simple subclass of AsyncTask) in onResume and cancel it in onPause if it is still executing. I received a crash report that made me wonder if the task cancel was working or not so added an analytics event to record onPostExecute getting called after onPause had cancelled the task.

Over the last month I have seen 140 of these events for 4,100 page views.

@Override
protected void onResume() {
    super.onResume();
    mIsResumed = true;
    if (mReverseCardsTask == null) {
        mReverseCardsTask = new TcgCursorTask(this) {
            @Override
            protected Cursor doInBackground(Void... params) {
                return mDb.reverseFetchCards();
            }
            @Override
            protected void onPostExecute(Cursor cursor) {
                if (mIsResumed) {
                    onReverseCardsCursor(cursor);
                } else {
                    EasyTracker.getTracker().sendEvent("error", "on-post-execute", "called after paused", 1L);
                }
            }
        };
        mReverseCardsTask.execute();
    }
}

@Override
protected void onPause() {
    super.onPause();
    mIsResumed = false;
    if (mReverseCardsTask != null) {
        mReverseCardsTask.cancel(false);
        mReverseCardsTask = null;
    }
}

I have a feeling I am missing something very simple here, but can't see it.

I just noticed I am not clearing mReverseCardsTask in onPostExecute, but that should not matter.

Was it helpful?

Solution 2

OK. I have worked it out. I am not sure which API version it was fixed in, but if you look at the code for Gingerbread there is a clear race condition in the cancel() handling. The GUI thread code which processes the MESSAGE_POST_RESULT message from the background calls onPostExecute() regardless of whether or not the task was cancelled.

It turns out that the fix is quite simple. All I need to do is add my own check of isCancelled() before executing my onPostExecute() logic.

The Gingerbread code receives MESSAGE_POST_RESULT and calls finish(). Then finish() calls onPostExecute().

OTHER TIPS

Just calling cancel() doesn't do anything. You actually have to put checks in the process to determine if it is to be canceled and do the job of canceling it.

The OS doesn't know what you may need to do to clean things up (like closing files or open network connections) before stopping.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top