虽然使用起来非常方便,但根据我的理解,AsyncTask
有两个重要的限制:
doInBackground
将共享同一个工作者
线程,即一个长时间运行的AsyncTasks
可以阻止所有其他线程。
execute
,onPostExecute
和其他“同步”方法必须/将始终在UI线程上执行,即不在要开始任务的线程上执行。
当我尝试在后台IntentService
中重用一些负责我的应用程序的客户端 - 服务器通信的现有AsyncTasks时,我遇到了麻烦。服务的任务将在工作线程中与UI活动的任务一起战斗。此外,他们会强制服务回退到UI线程,尽管该服务应该在后台安静地执行其工作。
我如何去除/规避这些限制?我基本上想要实现:
一个非常类似AsyncTask的框架(因为我需要在那里迁移大量关键代码)。
此类任务的每个实例都应在其自己的线程上运行doInBackground
,而不是为所有实例运行单个工作线程。
编辑:Thx向VinceFR指出这可以通过简单地拨打executeOnExecutor
而不是execute
来实现。
应该在启动任务的同一个线程上调用onPostExecute
之类的回调,调用execute
,这不应该是UI线程。
我想,我不是第一个要求这样的人。因此我想知道:是否已经有一些第三方库可以推荐实现这一目标?如果没有,那么实现这个的方法是什么?
提前致谢!
答案 0 :(得分:0)
解决方案如下所示:
产生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线程上。 Jelly 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
执行异步操作的许多现有类。