2013-03-08 3 views
0

다른 쪽이 완료 될 때까지 기다려야하는 2 개의 비동기 작업을 구현하려고합니다.AsyncTask가 deadlock없이 다른 것을 기다릴 수있는 방법

나는 다음과 같다 그것을위한 테스트 케이스합니다

public class MainActivity extends Activity{ 

    public static boolean SYNCING = false; 
    public static Object LOCK = new Object(); 

    public static void setSync(){ 
     Log.i(TAG, "setting Sync=true"); 
     synchronized (LOCK) { 
      SYNCING = true; 
     } 
    } 

    public static void waitForSync(){ 
     Log.i(TAG, "waiting for Sync"); 
     synchronized (LOCK) { 
      try{ 
       while (SYNCING){ 
        LOCK.wait(); 
       } 
      } catch (InterruptedException ignore){} 
     } 
    } 

    public static void setSyncFinish(){ 
     Log.i(TAG, "setSyncFinish to notify all"); 
     synchronized (LOCK) { 
      SYNCING = false; 
      LOCK.notifyAll(); 
     } 
    } 

    public class WaitingOnLoaderTask extends AsyncTask<Void, Void, Void>{ 

     @Override 
     protected Void doInBackground(Void... params) { 
      Log.i(TAG, "doInBackground of WaitingForLoaderTask"); 

      try { 
       Thread.sleep(500); 
      } catch (InterruptedException e) { 

      } 

      waitForSync(); 
      Log.i(TAG, "finishedWaiting!"); 
      return null; 
     } 

    } 

    public class LoaderTask extends AsyncTask<Void, Void, Void>{ 

     long mRuntime; 
     int mTimes; 

     public LoaderTask(long runtime, int times){ 
      mRuntime = runtime; 
      mTimes = times; 
     } 

     @Override 
     protected Void doInBackground(Void... params) { 
      while(mTimes >= 0){ 
       Log.i(TAG, "sleeping for " + mTimes + " times for " + mRuntime + " milliseconds"); 
       mTimes--; 
       try { 
        Thread.sleep(mRuntime); 
       } catch (InterruptedException e) { 
        Log.e("MainActivity", "Interrupted", e); 
       } 
      } 

      setSyncFinish(); 

      return null; 
     } 

    } 
} 

을하지만 난 WaitingOnLoaderTask가 제대로 기다려야 얻을 수 없습니다. 나는에서 onCreate에서 이것을 실행하면

new WaitingOnLoaderTask().execute((Void[])null); 
new LoaderTask(1000, 5).execute((Void[])null); 

나는 (교착 상태가 발생)이 같은 logOutput를 얻을 :

03-08 18:17:23.309: I/myUniqueLogTag(4432): setting Sync=true 
03-08 18:17:23.317: I/myUniqueLogTag(4432): doInBackground of WaitingForLoaderTask 
03-08 18:17:23.817: I/myUniqueLogTag(4432): waiting for Sync 

new LoaderTask(1000, 5).execute((Void[])null); 
new WaitingOnLoaderTask().execute((Void[])null); 

에 내가 같이 로그를 얻을 :

03-08 18:18:53.082: I/myUniqueLogTag(4621): sleeping for 5 times for 1000 milliseconds 
03-08 18:18:54.082: I/myUniqueLogTag(4621): sleeping for 4 times for 1000 milliseconds 
03-08 18:18:55.090: I/myUniqueLogTag(4621): sleeping for 3 times for 1000 milliseconds 
03-08 18:18:56.082: I/myUniqueLogTag(4621): sleeping for 2 times for 1000 milliseconds 
03-08 18:18:57.090: I/myUniqueLogTag(4621): sleeping for 1 times for 1000 milliseconds 
03-08 18:18:58.106: I/myUniqueLogTag(4621): sleeping for 0 times for 1000 milliseconds 
03-08 18:18:59.113: I/myUniqueLogTag(4621): doInBackground of WaitingForLoaderTask 
03-08 18:18:59.113: I/myUniqueLogTag(4621): setSyncFinish to notify all 
03-08 18:18:59.621: I/myUniqueLogTag(4621): waiting for Sync 
03-08 18:18:59.621: I/myUniqueLogTag(4621): finishedWaiting! 

내가 추측하는 것은 실행할 것이다 theres. 그들은 실제로 동일한 스레드에서 doInBackground을 계산 했습니까? LOCK.wait()은이 스레드가 더 이상 실행되지 않도록 차단합니까?

해결 방법이 있습니까 (Android 2.2와 호환 가능)?

참고 : 두 번째 시간은 기본적으로 원하는 것입니다. 그러나 다른 작업 구현에서는 다른 하나가 완료되었을 때만 WaitingOnLoaderTask이 실행되도록 설정할 수 없습니다.

+2

"다른 비동기 작업을 기다려야하는 2 개의 비동기 작업을 구현하려고합니다."- 왜 단지 하나의 AsyncTask가 아닌가? – CommonsWare

+0

다른 하나의'onPostExecute()'에서 호출 된 것이거나, 작업을 별도로 유지하고자한다면. – Geobits

+0

불가능합니다. "realworld usage"에서 첫 번째 작업은 서버와 사물을 동기화하고 결과를 DB에 저장하는 것입니다. 'Application'에서 호출됩니다. 다른 하나는 db로부터이 데이터를 수신하지만, 동기화가 완료되기 전에 첫 번째 동기화가 완료 될 때까지 기다려야하고 호출 될 수 있습니다. 'onPostExecute'에서 두 번째를 시작하는 것은 아무런 의미가 없습니다. –

답변

1

가장 좋은 방법은 Geobits 제안을 사용하는 것입니다.

그것을 수행 할 수 있도록 당신은 다음 비동기 작업을 호출 할 onPostExecute() 함수를 사용할 수 있습니다

class WaitingOnLoaderTask extends AsyncTask<Void,Void,Void> 
{ 
    protected void onPreExecute(Void) 
    { 
     super.onPreExecute(); 
    } 

    protected void doInBackground(Void... params) 
    { 
     //Commands for first AsyncTask 
    } 

    protected void onPostExecute(Void) 
    { 
     //Call AsyncTask 2 
    } 
} 

그 방법을이 작업 1이 완료 될 때까지 기다립니다.

+0

스레드를 공유하고있는 것으로 밝혀지면 내가 할 것입니다 : 이것은 두 번째가 APP 내에서 목적 상으로 호출되었으므로 가능하지 않습니다. 첫 번째는'Application '!!! –

+0

@RafaelT : 첫 번째 작업은'new LoaderTask (1000, 5) .execute ((Void []) null) .get();'으로 실행되고, 처음 완료 될 때까지 기다리지 않습니다. –

+0

'AsyncTask.get'가 차단됩니다. 'ANR'을 야기 할'MainThread' –

1

Android 3.2 이하에서 코드가 예상대로 작동합니다.

LoaderTask loaderTask = new LoaderTask(1000, 5); 
WaitingOnLoaderTask waitingTask = new WaitingOnLoaderTask(); 
if (Build.VERSION.SDK_INT >= 14) { 
    loaderTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 
    waitingTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 
} else { 
    loaderTask.execute(); 
    waitingTask.execute(); 
} 

참고 : 당신은 구성 변경 (예를 들어, 회전)에 활동 누출 방지하기 위해 static로 AsyncTasks를 표시해야합니다 높은 안드로이드 버전에 AsyncTasks는 4 + 안드로이드에서 작동하도록이 시도 병렬 실행되지 않습니다.