2

while it is very convenient to use, from my understanding, AsyncTask has two important limitations:

  1. doInBackground of any instances will share the same worker thread, i.e. one long running AsyncTasks can block all others.

  2. execute, onPostExecute and other "synchronizing" methods must/will always be executed on the UI-thread, i.e. not on the Thread, which wants to start the task.

I ran into trouble, when I tried to reuse some existing AsyncTasks in a background IntentService that are responsible for the client-server communication of my app. The tasks of the service would fight over time in the worker thread with those of the UI activities. Also they would force the service to fall back onto the UI-thread, although that service should perform its work quietly in the background.

How would I go about removing/circumventing these limitations? I basically want to achieve:

  1. A framework that closely resembles AsyncTask (because I need to migrate a lot of critical code there).

  2. Each instance of such a task should run its doInBackground on its own thread instead of a single worker thread for all instances.

    Edit: Thx to VinceFR for pointing out this can be achieved by simply calling executeOnExecutor instead of execute.

  3. The callbacks like onPostExecute should be called on the same thread that started the task by calling execute, which should not need to be the UI-thread.

I figure, I'm not the first person to require something like this. Therefore I wonder: Is there already some third-party library that can be recommended to accomplish this? If not, what would be a way to implement this?

Thanks in advance!

4

1 回答 1

0

解决方案如下所示:

所有产生AsyncTask可能相互干扰的类都有自己的Executor类(如您喜欢使用线程池等进行详细说明):

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

正如 VinceFR 所指出的,您可以通过这样调用它来运行一个AsyncTask给定的(您会定期传递给任务的参数在哪里):Executorpayload

task.executeOnExecutor(serviceExecutor, payload);

但是,这破坏了对 Gingerbread 和更早版本的向后兼容性。另外,如果你想支持 Honeycomb,你需要确保这个调用发生在 UI 线程上。Jelly Bean 会自动处理这个问题。

现在更棘手的部分是:让服务在自己的线程上运行。与 Android 中的许多事情一样,这似乎比它需要的更难(或者我可能在这里缺少一些信息)。你不能使用 an ,因为它会在 an 第一次接管IntentService时自动关闭,让回调完成。AsyncTaskonHandleIntent

您需要在服务上设置自己的线程和事件循环:

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
}

所以,这对我有用。我很高兴看到一个更简单的解决方案。请注意,该解决方案要求服务知道将由ExistingClassWithAsyncOperationUI 线程回调。我不是特别喜欢这种依赖,但现在不知道如何做得更好。但是,我不必重写许多使用AsyncTask.

于 2013-03-19T08:32:25.497 回答