2013-03-15 47 views
2

而它使用起來非常方便,從我的理解,AsyncTask有兩個重要的限制:如何實現更靈活的AsyncTask?

  1. 任何情況下的doInBackground將共享相同的工人 線程,也就是一個長期運行的AsyncTasks可以阻止所有其他人。

  2. executeonPostExecute和其他「同步」方法必須/將始終在UI線程上執行,即不在要啓動任務的線程上執行。

我遇到了麻煩,當我試圖重用的背景IntentService,負責我的應用程序的客戶端服務器通信的一些現有AsyncTasks。該服務的任務將隨着時間在工作線程中與UI活動的任務進行鬥爭。他們也會強制服務回落到UI線程中,儘管該服務應該在後檯安靜地執行它的工作。

我將如何去除/規避這些限制?我基本上想實現:

  1. 一個框架,非常類似於AsyncTask(因爲我需要遷移很多關鍵代碼)。

  2. 此類任務的每個實例應該在其自己的線程上運行其doInBackground,而不是針對所有實例的單個工作線程。

    編輯:Thx to VinceFR指出這可以通過簡單地調用executeOnExecutor而不是execute來實現。

  3. onPostExecute的回調應該是通過調用execute,不應需要是UI線程啓動任務在同一個線程調用。

我想,我不是第一個需要這樣的東西的人。因此我想知道:是否已經有一些第三方庫可以被推薦來實現這個目標?如果不是,那麼實現這個的方法是什麼?

在此先感謝!

+1

https://github.com/octo-online/robospice。替代asynctask – Raghunandan 2013-03-15 08:15:10

+0

謝謝,我在文檔中挖了一點點。 RoboSplice似乎也會在UI線程中回調您。此外,該框架不會允許輕鬆遷移。但是,對於任何新代碼來說,這似乎是一個非常好的方法。 – Chris 2013-03-15 08:29:36

+1

供參考:如果您使用'executeOnExecutor'而不是'execute'(函數doc [here] [1]),任何實例的doInBackground將不會共享相同的線程 [1]:http://developer.android.com /reference/android/os/AsyncTask.html#executeOnExeoror%28java.util.concurrent.Executor,%20Params...%29 – VinceFR 2013-03-15 08:47:21

回答

0

該解決方案是這樣的:

所有類產卵AsyncTask s表示可能會相互干擾獲得自己Executor像這樣的(做出精心爲你喜歡使用線程池等):

private Executor serviceExecutor = new Executor() { 
    public void execute(Runnable command) { 
     new Thread(command).start(); 
    } 
}; 

正如指出的VinceFR可以通過調用它像這樣運行在一個給定的ExecutorAsyncTask(其中​​是,你會經常傳遞給任務的參數):

task.executeOnExecutor(serviceExecutor, payload); 

但是,這會打破向後兼容薑餅和更早。另外,如果你想支持Honeycomb,你需要確保這個調用發生在UI線程上。果凍豆將自動照顧這一點。

現在更棘手的部分:保持服務運行在自己的線程上。 Android中的許多事情似乎比它需要的更困難(或者我在這裏缺少一些信息)。您不能使用IntentService,因爲在第一次AsyncTask接管時會自動關閉,並且讓onHandleIntent回調完成。

您需要設置在服務自己的線程和事件循環:

public class AsyncService extends Service { 

private static final String TAG = AsyncService.class.getSimpleName(); 
private class LooperThread extends Thread { 
    public Handler threadHandler = null; 

    public void run() { 
     Looper.prepare(); 
     this.threadHandler = new Handler(); 
     Looper.loop(); 
    } 
} 
private LooperThread serviceThread = null; 
private Handler serviceThreadHandler = null; 

@Override 
// This happens on the UI thread 
public void onCreate() { 
    super.onCreate(); 
} 

@Override 
// This happens on the UI thread 
public int onStartCommand(Intent intent, int flags, int startId) { 
    this.serviceThread = new LooperThread(); 
    this.serviceThread.start(); 
    while(this.serviceThread.threadHandler == null) { 
     Log.d(TAG, "Waiting for service thread to start..."); 
    } 
    this.serviceThreadHandler = this.serviceThread.threadHandler; 
    this.serviceThreadHandler.post(new Runnable() { 
     @Override 
     public void run() { 
      doTheFirstThingOnTheServiceThread(); 
     } 
    }); 
    return Service.START_STICKY; 
} 

// doTheFirstThingOnTheServiceThread 

} 

不,你需要確保每個時間AsyncTask返回到UI線程,你在你的服務線程結束,而不是:

// This happens on the serviceThread 
private void doTheFirstThingOnTheServiceThread() { 
    // do some stuff 

    // here we can reuse a class that performs some work on an AsyncTask 
    ExistingClassWithAsyncOperation someUsefullObject = new ExistingClassWithAsyncOperation(); 
    // the existing class performs some work on an AsyncTask and reports back via an observer interface 
    someUsefullObject.setOnOperationCompleteListener(new OnOperationCompleteListener() { 
     @Override 
     // This happens on the UI thread (due to an ``AsyncTask`` in someUsefullObject ending) 
     public void onOperationComplete() { 
      serviceThreadHandler.post(new Runnable() { 
       @Override 
       public void run() { 
        doTheSecondThingOnTheServiceThread(); 
       } 
      }); 
     } 
    } 
    someUsefulObject.performOperation(); 
} 

// This happens on the serviceThread 
private void doTheSecondThingOnTheServiceThread() { 
    // continue working on the serviceThread 
} 

所以,這對我的作品。我很高興看到這個更簡單的解決方案。請注意,該解決方案需要服務知道將由UI線程上的ExistingClassWithAsyncOperation回調。我不特別喜歡這種依賴性,但現在不知道如何做得更好。但是,我不必重寫很多使用AsyncTask執行異步操作的現有類。