虽然使用起来非常方便,但据我所知,AsyncTask
有两个重要限制:
任何实例的
doInBackground
将共享同一工作线程线程,即一个长时间运行的
AsyncTasks
可以阻止所有其他线程。execute
,onPostExecute
和其他“同步”方法必须/将始终在要启动任务的UI线程(即不在线程)上执行。当我尝试在后台
IntentService
中重用一些现有的AsyncTasks来负责我的应用程序的客户端-服务器通信时,我遇到了麻烦。随着时间的推移,服务的任务将在工作线程中与UI Activity 的任务进行争夺。同样,它们将迫使服务退回到UI线程上,尽管该服务应在后台安静地执行其工作。我将如何消除/规避这些限制?我基本上想实现:
doInBackground
,而不是对所有实例使用单个工作线程。编辑:Thx到VinceFR指出这可以通过简单地调用
executeOnExecutor
而不是execute
来实现。onPostExecute
在启动任务的同一线程上调用类似execute
的回调,该线程不必是UI线程。我想,我不是第一个要求这样的人。因此,我想知道:是否已经可以推荐一些第三方库来完成此任务?如果没有,那么实现此目标的方法是什么?
提前致谢!
最佳答案
解决方案如下所示:
所有产生AsyncTask
的类都可能会相互干扰,它们都会得到自己的Executor
,就像这样(使用线程池等进行详细说明):
private Executor serviceExecutor = new Executor() {
public void execute(Runnable command) {
new Thread(command).start();
}
};
正如VinceFR所指出的,您可以通过像这样调用它来在给定的
AsyncTask
上运行Executor
(其中payload
是您将定期传递给任务的参数):task.executeOnExecutor(serviceExecutor, payload);
但是,这破坏了与Gingerbread及更早版本的向后兼容性。另外,如果要支持Honeycomb,则需要确保此调用在UI线程上进行。果冻 bean 将自动处理此问题。
现在比较棘手的部分:使服务在其自己的线程上运行。由于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
执行异步操作的现有类。
关于android - 如何实现更灵活的AsyncTask?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15427501/