2013-06-22 2 views
1

SETUP:AsyncTask запущен из onPostExecute другого AsyncTask не выполняется должным образом в API 10

У меня есть одноэлементный класс приложения, который имеет метод с именем fetchUpdates(). Этот метод вызывается классом UpdaterService (IntentService). fetchUpdates() метод просто вызывает DownloadDataAsyncTask, который затем вызывает UpdateDbAsyncTask во время onPostExecute, если данные были успешно загружены. Оба AsyncTasks находятся в классе приложения (не уверен, что это связано с проблемой).

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

класс 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(); 
     } 
    } 
} 

класс 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); 
    } 
} 

ПРОБЛЕМА:

Все отлично работает в API 16, но исполнение UpdateDbAsyncTask привалах после doInBackground в API 10. Метод 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. 

ДАЛЕЕ ИССЛЕДОВАНИЯ

threading rules для AsyncTask состояния, что:

  • Класс AsyncTask должен быть загружен в потоке пользовательского интерфейса. Это делается автоматически с JELLY_BEAN.
  • Экземпляр задачи должен быть создан в потоке пользовательского интерфейса.
  • execute (Params ...) должен быть вызван в потоке пользовательского интерфейса.
  • и т.д ...

В моем понимании (это не так много, только начал программирование) первый UpdateDbAsyncTask выполнен полностью в обоих API, потому что он не нарушает каких-либо правил пронизывающих. Но второй из них либо нарушает одно из правил потоковой передачи, заставляя его прекратить выполнение после doInBackground, либо это что-то еще полностью вне моего понимания.

Я также нашел кого-то еще с аналогичным issue. И позже он был предложен в вопросе к

просто используя класс AsyncTask для API15 + в мой код во все времена

Но я не совсем уверен, как сделать что либо.

Любая помощь будет высоко оценена. Благодаря!

UPDATE 1

Проблема возникает только тогда, когда я называю класс UpdaterService IntentService (который вызывает метод fetchUpdates() в классе Application) от деятельности. Код у меня в MainActivity ниже:

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

Однако, когда я назвал fetchUpdates() непосредственно с помощью кода

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

из MainActivity, все выполнено отлично в API 10.

А также обратите внимание, что также вызывается с использованием класса BroadcastReceiver, который устанавливается через регулярные интервалы с использованием повторяющегося сигнала тревоги. В этом случае AsyncTasks выполняются как ожидалось ... очень странно.

UPDATE 2

Ответ на Streets Of Boston, кажется, правильный ответ. Но, как ни странно, следующий, кажется, работает так же:

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

Я использовал приведенный выше код, чтобы запустить IntentService из MainActivity вместо startService(new Intent(this, UpdaterService.class)); и проблема, кажется, не сохраняются.

+0

http://bon-app-etit.blogspot.in/2013/04/the-dark-side-of-asynctask.html –

+1

Это работает, потому что вы теперь вызываете startService из Activity, а Activity - это так называемый компонент переднего плана. Жизненный цикл службы, которую вы запускаете, теперь привязан к жизненному циклу Деятельности, и пока деятельность находится на переднем плане (то есть видна), ваша служба не будет убита/уничтожена. –

ответ

6

Прежде всего, выполнение кода в IntentService уже выполняется в фоновом потоке. Нет необходимости использовать AsyncTask.

Я не удивлен, что вы получили эту ошибку. Я удивлен, что вторая ваша AsyncTask вызывается вообще. Когда ваш IntentService завершит вызов метода fetchUpdates, ваша служба будет завершена, и ваш процесс и/или потоки могут быть запланированы для уничтожения/уничтожения. В зависимости от того, насколько агрессивна ОС, это может произойти немедленно или через несколько минут. Это означает, что ваш код будет запущен на мертвой ветке, и это то, что происходит.

Не используйте AsyncTasks из IntentService. Вместо этого просто вызовите код из этих AsyncTasks непосредственно в IntentService.

1

Решение простое, в postExecute из DownloadDataAsyncTask, просто сделать это:

@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(); 
    } 
} 
+0

Человек, ты меня там потерял. Я относительно новый программист. Не могли бы вы немного его сформулировать (о размещении там новой функции)? Благодарю. – fahmy

+0

Btw, оба AsyncTasks находятся в классе Application, поэтому 'runOnUiThread' недоступен в AsyncTasks. – fahmy

+0

Ха-ха, это нормально, если вы новичок: P, я только что отредактировал ответ, я думаю, если бы вы просто изменили эту функцию на то, что я только что положил, она должна работать нормально. – LuckyMe

0

в doInBackground() из UpdateDbAsyncTask() код класса записи потоков внутри rubInUiThread()