从另一个AsyncTask的onPostExecute启动的asyncTask在API 10中无法正确执行

fah*_*hmy 1 android asynctaskloader

建立:

我有一个单例应用程序类,它有一个名为的方法fetchUpdates().此方法由UpdaterService(IntentService)类调用.fetchUpdates()方法只是调用DownloadDataAsyncTask,然后UpdateDbAsyncTaskonPostExecute数据成功下载期间调用.两个AsyncTasks都驻留在应用程序类中(不确定这是否与问题相关).

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

DownloadDataAsyncTask类

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();
         }
    }
}
Run Code Online (Sandbox Code Playgroud)

UpdateDbAsyncTask类

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

问题:

在API 16中一切正常,但UpdateDbAsyncTask在API 10中的doInBackground之后执行暂停.也不调用onCancelled(Object)方法.

LOGCAT输出

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.
Run Code Online (Sandbox Code Playgroud)

进一步的研究

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));
Run Code Online (Sandbox Code Playgroud)

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

MyApplication myApp = (MyApplication) getApplication();
myApp.fetchUpdates();
Run Code Online (Sandbox Code Playgroud)

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);
    }
});
Run Code Online (Sandbox Code Playgroud)

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.

Str*_*ton 6

首先,IntentService中的代码执行已经在后台线程中运行.没有必要使用AsyncTask.

你得到这个错误我并不感到惊讶.我很惊讶你的第二个AsyncTask被调用了.当您的IntentService完成调用fetchUpdates方法时,您的服务已完成,您的进程和/或线程可能被安排销毁/终止.根据操作系统的激进程度,可能会立即或几分钟后发生.这意味着你的代码将在一个死线程上运行,这就是所发生的事情.

解决方案:不要在IntentService中使用AsyncTasks.相反,只需直接从IntentService中的AsyncTasks调用代码即可.