2013-06-27 2 views
1

Я работаю с Android-приложением, которое должно выполнить запрос сервера, а затем выполнить действия, когда запрос будет завершен. Вот некоторые псевдо-код, чтобы объяснить ситуацию:Лучший способ гарантировать обратный вызов всегда выполняется асинхронно?

makeRequest(new SomeTask{ 
    onDone() { 
     // Do actionB with queue 
    } 
}); 

// Do actionA with queue. Must be execute first!! 

Вот реализация makeRequest в псевдокоде:

makeRequest(SomeTask task) { 
    if(canDoOptimization) { // if true, don't need to make request 

     // It's a bad idea to execute this immediately. 
     // Wish I could wait until the current thread of execution was done... 
     task.onDone(); 
     return; 
    } 

    asyncTask = new AsyncTask<SomeTask, Void, Void>() { 
     doInBackground(SomeTask... task) { 
      // Make server request... 

      task.onDone(); 
     } 
    } 
    asyncTask.execute(task); 
} 

Обычно actionA происходит до actionB, как и ожидалось, но в тех случаях, когда мы можем избежать сети запросы, SomeTask.execute вызывается немедленно. Это приводит к возникновению actionBдоactionA, что плохо. Есть ли способ гарантировать, что этого не произойдет?

Я столкнулся с этой ситуацией несколько раз в javascript. В таких случаях я бы обернул вызов SomeTask.executesetTimeout или setImmediate, чтобы сохранить правильную асинхронную семантику.

Для ясности, вот пример того же ошибка в JavaScript: https://gist.github.com/xavi-/5882483

Любая идея, что я должен делать в Java/Android?

+0

Почему вы не можете называть действие А до того makeRequest? –

+0

Это возможность, но это потребует значительного рефакторинга. Кроме того, я надеялся, что существует общее решение, поэтому я бы не стал помнить об этой проблеме. – Xavi

+0

Установка тайм-аута кажется очень храбрым решением для этого –

ответ

1

Добро пожаловать в мир синхронизации. Для этой цели часто используются объекты Mutex или lock. Is there a Mutex in Java?

ваше задание B должно ждать на мьютексе, которое должно быть сообщено по задаче A по его завершению. Это обеспечит надлежащий порядок выполнения, когда задание будет завершено до B.

1

Всегда помещайте task.onDone() в AsyncTask, даже если ему не нужно делать запрос.

makeRequest(SomeTask task) { 

    asyncTask = new AsyncTask<SomeTask, Void, Void>() { 
     void doInBackground(SomeTask... task) { 

      if(canDoOptimization) { // if true, don't need to make request 

       // It's a bad idea to execute this immediately. 
       // Wish I could wait until the current thread of was done... 
       task.onDone(); 
       return; 
      } else { 
       // Make server request... 

       task.onDone(); 
      } 
     } 
    } 
    asyncTask.execute(task); 
} 
+0

Это имеет большой смысл. К сожалению, я более упростил мой пример. Проверка «canDoOptimization» и фактический сетевой вызов происходят в разных местах. Объект 'SomeTask' передается совсем немного. Возможно, я могу просто завершить вызов 'task.onDone()' в манекене 'AsyncTask'? – Xavi

+0

Манекен AsyncTask стоит того, чтобы выстрелить. Я на самом деле не специалист по Android, я снимал в темноте :) – jimbojw

0

Почему вы не можете просто переключить порядок вещей?

// Do actionA with queue. Must be execute first!! 

makeRequest(new SomeTask{ 
onDone() { 
    // Do actionB with queue 
}); 

Если actionA является асинхронным, а также и выполняется на отдельном AsyncTask, вы можете позвонить makeRequest (...) по методу() actionA в AsyncTasks onPostExecute игровая.

И, кстати, с Android Honeycomb версия AsyncTasks запускается в том же потоке, то есть если у вас есть несколько задач, они могут блокировать друг друга. Зафиксировано, указав, что AsyncTsak должен работать в пуле потоков:

if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.HONEYCOMB) { 
asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 
} 
else { 
    asyncTask.execute(); 
}