An asyncTask launched from an onPostExecute of another AsyncTask does not execute properly in API 10

拈花ヽ惹草 提交于 2019-12-11 07:51:16

问题


SETUP:

I have a singleton application class that has a method named fetchUpdates(). This method is called by an UpdaterService (an IntentService) class. fetchUpdates() method simply calls DownloadDataAsyncTask which then calls UpdateDbAsyncTask during onPostExecute if the data were successfully downloaded. Both AsyncTasks reside in the application class (not sure if this is relavant to the problem).

public synchronized void fetchUpdates() {
    String[] mURLs = getURLs();
    new DownloadDataAsyncTask().execute(mURLs[0], mURLs[1]);
}

DownloadDataAsyncTask class

private class DownloadDataAsyncTask extends AsyncTask<String, Void, JSONObject[]> {

    @Override
    protected synchronized JSONObject[] doInBackground(String... params) {
        String urlData = (String) params[0];
        String urlId = (String) params[1];
        JSONObject[] jSON = new JSONObject[] {
                JSONfunctions.getJSONfromURL(urlData),
                JSONfunctions.getJSONfromURL(urlId) };
        return mJSON;
    }

    @Override
    protected void onPostExecute(JSONObject[] result) {
         if (result[0] != null && result[1] != null) {
             new UpdateDbAsyncTask().execute(result[0], result[1]);
         } else {
             displayFailureToast();
         }
    }
}

UpdateDbAsyncTask class

private class UpdateDbAsyncTask extends AsyncTask<JSONObject, Void, int[]> {

    @Override
    protected synchronized int[] doInBackground(JSONObject... params) {
        Log.d(TAG, "UpdateDbAsyncTask doInBackground BEGIN");
        int[] info = updateDb(params[0], params[1]);
        Log.d(TAG, "UpdateDbAsyncTask doInBackground RETURNING RESULT");
        return info
    }

    @Override
    protected void onPostExecute(int[] result) {
        Log.d(TAG, "UpdateDbAsyncTask onPostExecute BEGIN");
        if (result[0] > 0) makeNotification(0);
        if (result[1] > 0) makeNotification(1);
    }
}

PROBLEM:

Everything works fine in API 16, but execution of UpdateDbAsyncTask halts after the doInBackground in API 10. The onCancelled(Object) method is not called either.

LOGCAT OUTPUT

06-22 16:11:51.047: D/dalvikvm(499): VFY: replacing opcode 0x6e at 0x0089
06-22 16:11:51.057: D/dalvikvm(499): VFY: dead code 0x008c-008e in Lcom/daybreak/android/test/MyApplication$UpdateDbAsyncTask;.onPostExecute ([I)V
06-22 16:11:51.087: D/MyApplication(499): UpdateDbAsyncTask doInBackground BEGIN
06-22 16:11:51.187: D/MyApplication(499): UpdateDbAsyncTask doInBackground RETURNING RESULT
06-22 16:11:51.197: W/MessageQueue(499): Handler{40567510} sending message to a Handler on a dead thread
06-22 16:11:51.197: W/MessageQueue(499): java.lang.RuntimeException: Handler{40567510} sending message to a Handler on a dead thread
06-22 16:11:51.197: W/MessageQueue(499):    at android.os.MessageQueue.enqueueMessage(MessageQueue.java:196)
06-22 16:11:51.197: W/MessageQueue(499):    at android.os.Handler.sendMessageAtTime(Handler.java:457)
06-22 16:11:51.197: W/MessageQueue(499):    at android.os.Handler.sendMessageDelayed(Handler.java:430)
06-22 16:11:51.197: W/MessageQueue(499):    at android.os.Handler.sendMessage(Handler.java:367)
06-22 16:11:51.197: W/MessageQueue(499):    at android.os.Message.sendToTarget(Message.java:349)
06-22 16:11:51.197: W/MessageQueue(499):    at android.os.AsyncTask$3.done(AsyncTask.java:214)
06-22 16:11:51.197: W/MessageQueue(499):    at java.util.concurrent.FutureTask$Sync.innerSet(FutureTask.java:253)
06-22 16:11:51.197: W/MessageQueue(499):    at java.util.concurrent.FutureTask.set(FutureTask.java:113)
06-22 16:11:51.197: W/MessageQueue(499):    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:311)
06-22 16:11:51.197: W/MessageQueue(499):    at java.util.concurrent.FutureTask.run(FutureTask.java:138)
06-22 16:11:51.197: W/MessageQueue(499):    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1088)
06-22 16:11:51.197: W/MessageQueue(499):    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:581)
06-22 16:11:51.197: W/MessageQueue(499):    at java.lang.Thread.run(Thread.java:1019)
06-22 16:11:55.717: W/GAV2(499): Thread[Service Reconnect,5,main]: Service unavailable (code=1), using local store.

FURTHER RESEARCH

The threading rules for AsyncTask state that:

  • The AsyncTask class must be loaded on the UI thread. This is done automatically as of JELLY_BEAN.
  • The task instance must be created on the UI thread.
  • execute(Params...) must be invoked on the UI thread.
  • etc...

To my understanding (which is not much, just started programming) the first UpdateDbAsyncTask is executed completely in both APIs because it does not violate any of the threading rules. But the second one is either violating one of the threading rules, causing it to halt execution after doInBackground, or it is something else completely beyond my understanding.

I also found someone else with a similar issue. And later it was suggested in the question to

simply using AsyncTask class for API15+ in my code at all times

But I'm not quite sure how to do that either.

Any help would be much appreciated. Thanks!

UPDATE 1

The problem only arises when I call the UpdaterService IntentService class (which calls the fetchUpdates() method in the Application class) from an Activity. The code I had in my MainActivity is below:

startService(new Intent(this, UpdaterService.class));

However, when I called fetchUpdates() directly using the code

MyApplication myApp = (MyApplication) getApplication();
myApp.fetchUpdates();

from the MainActivity, everything is executed perfectly in API 10.

And also note that the UpdaterService is also invoked using a BroadcastReceiver class which is set be called at regular intervals using a repeating alarm. In this case the AsyncTasks are executed as expected... very strange.

UPDATE 2

The answer by Streets Of Boston seems to be the correct answer. But, curiously, the following seems to work as well:

runOnUiThread(new Runnable() {
    public void run() {
        Intent serviceIntent = new Intent();
        serviceIntent.setClass(getApplicationContext(), UpdaterService.class);
        startService(serviceIntent);
    }
});

I used the above code to start the IntentService from the MainActivity instead of startService(new Intent(this, UpdaterService.class)); and the problem does not seem to persist.


回答1:


First of all, the execution of code in an IntentService already runs in a background thread. There is no need to use AsyncTask.

I'm not surprised you get this error. I'm surprised that your second AsyncTask gets called at all. When your IntentService finishes calling the fetchUpdates method, your service is done and your process and/or threads could be scheduled to be destroyed/killed. Depending on how aggressive the OS is, this may happen immediately or minutes later. This means that your code gets to be run on a dead thread, and that's what happens.

Solution: Don't use AsyncTasks from within IntentService. Instead, just call the code from those AsyncTasks in your IntentService directly.




回答2:


The solution is simple, in the postExecute of DownloadDataAsyncTask, simple do this:

@Override
protected void onPostExecute(final JSONObject[] result) {
     if (result[0] != null && result[1] != null) {
         runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 new UpdateDbAsyncTask().execute(result[0], result[1]);
             }
         });
     } else {
         displayFailureToast();
     }
}



回答3:


in doInBackground() of UpdateDbAsyncTask() thread class write code inside rubInUiThread()



来源:https://stackoverflow.com/questions/17250180/an-asynctask-launched-from-an-onpostexecute-of-another-asynctask-does-not-execut

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!