问题
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