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.