如何实现更灵活的AsyncTask?

时间:2013-03-15 08:11:39

标签: android multithreading android-asynctask ui-thread

虽然使用起来非常方便,但根据我的理解,AsyncTask有两个重要的限制:

    任何实例的
  1. doInBackground将共享同一个工作者 线程,即一个长时间运行的AsyncTasks可以阻止所有其他线程。

  2. executeonPostExecute和其他“同步”方法必须/将始终在UI线程上执行,即不在要开始任务的线程上执行。

  3. 当我尝试在后台IntentService中重用一些负责我的应用程序的客户端 - 服务器通信的现有AsyncTasks时,我遇到了麻烦。服务的任务将在工作线程中与UI活动的任务一起战斗。此外,他们会强制服务回退到UI线程,尽管该服务应该在后台安静地执行其工作。

    我如何去除/规避这些限制?我基本上想要实现:

    1. 一个非常类似AsyncTask的框架(因为我需要在那里迁移大量关键代码)。

    2. 此类任务的每个实例都应在其自己的线程上运行doInBackground,而不是为所有实例运行单个工作线程。

      编辑:Thx向VinceFR指出这可以通过简单地拨打executeOnExecutor而不是execute来实现。

    3. 应该在启动任务的同一个线程上调用onPostExecute之类的回调,调用execute,这不应该是UI线程。

    4. 我想,我不是第一个要求这样的人。因此我想知道:是否已经有一些第三方库可以推荐实现这一目标?如果没有,那么实现这个的方法是什么?

      提前致谢!

1 个答案:

答案 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执行异步操作的许多现有类。