Android AsyncTask实现

Stella981
• 阅读 1019

AsyncTask是android中一个非常好用的异步执行工具类。

AsyncTask的应用

AsyncTask enables proper and easy use of the UI thread. 这个类允许执行后台操作并在UI线程中发布结果,而却不需要管理threads和/或handlers。

AsyncTask被设计为一个围绕着[Thread](https://www.oschina.net/action/GoToLink?url=http%3A%2F%2Fdeveloper.android.com%2Freference%2Fjava%2Flang%2FThread.html)和````[Handler](https://www.oschina.net/action/GoToLink?url=http%3A%2F%2Fdeveloper.android.com%2Freference%2Fandroid%2Fos%2FHandler.html)的辅助类,而不是一个通用的线程框架。AsyncTask理想的应用场景应该是耗时短的操作(最多几秒钟)。如果你需要使线程长时间的运行,则强烈建议你使用**java.util.concurrent**包提供的各种APIs,比如ExecutorThreadPoolExecutor和``FutureTask`。```

一个异步任务由一个在后台线程中执行的计算来定义,而其结果将在UI线程中发布。一个异步任务由3种泛型,称为`Params`, `Progress` 和 ``Result,和4个步骤,称为`onPreExecute`,`doInBackground`,`onProgressUpdate` 和 `onPostExecute`,来定义。``

开发者指南

更多关于使用tasks和threads的信息,可以阅读Processes和Threads 开发者指南。

使用

必须通过继承来使用AsyncTask。子类将覆写至少一个方法([doInBackground(Params...)](https://www.oschina.net/action/GoToLink?url=http%3A%2F%2Fdeveloper.android.com%2Freference%2Fandroid%2Fos%2FAsyncTask.html%23doInBackground%28Params...%29)),而大多数情况下将覆写第二个([onPostExecute(Result)](https://www.oschina.net/action/GoToLink?url=http%3A%2F%2Fdeveloper.android.com%2Freference%2Fandroid%2Fos%2FAsyncTask.html%23onPostExecute%28Result%29))。

这里是一个继承的例子:

 private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
     protected Long doInBackground(URL... urls) {
         int count = urls.length;
         long totalSize = 0;
         for (int i = 0; i < count; i++) {
             totalSize += Downloader.downloadFile(urls[i]);
             publishProgress((int) ((i / (float) count) * 100));
             // Escape early if cancel() is called
             if (isCancelled()) break;
         }
         return totalSize;
     }
     protected void onProgressUpdate(Integer... progress) {
         setProgressPercent(progress[0]);
     }
     protected void onPostExecute(Long result) {
         showDialog("Downloaded " + result + " bytes");
     }
 }

创建之后,一个task的执行则非常简单:

new DownloadFilesTask().execute(url1, url2, url3);

AsyncTask的泛型

一个异步任务使用的三种类型如下:

  1. Params,任务执行时发送给它的参数的类型

  2. Progress,后台计算过程中发布的进度单元的类型。

  3. Result,后台计算的结果的烈性。

一个异步任务不总是会用到所有的类型。要标记一个类型未被使用,则简单的使用[Void](https://www.oschina.net/action/GoToLink?url=http%3A%2F%2Fdeveloper.android.com%2Freference%2Fjava%2Flang%2FVoid.html):

private class MyTask extends AsyncTask<Void, Void, Void> { ... }

4个步骤

当一个异步任务被执行时,则它会历经4个步骤:

  1. [onPreExecute()](https://www.oschina.net/action/GoToLink?url=http%3A%2F%2Fdeveloper.android.com%2Freference%2Fandroid%2Fos%2FAsyncTask.html%23onPreExecute%28%29),任务被执行之前在UI线程中调用。这个步骤通常被用来设置任务,比如在UI中显示一个进度条。

  2. [doInBackground(Params...)](https://www.oschina.net/action/GoToLink?url=http%3A%2F%2Fdeveloper.android.com%2Freference%2Fandroid%2Fos%2FAsyncTask.html%23doInBackground%28Params...%29)onPreExecute()结束执行之后,这个方法会立即在后台线程中被调用。这个步骤用于执行可能需要比较长时间的后台的计算。异步任务的参数会被传递给这个步骤。计算的结果必须由这个步骤返回,并将会被传回给最后的步骤。这一步中也可以使用publishProgress(Progress...)来发布一个或多个的进度单元。这些值在UI线程中发布,在onProgressUpdate(Progress...)一步中。

  3. [onProgressUpdate(Progress...)](https://www.oschina.net/action/GoToLink?url=http%3A%2F%2Fdeveloper.android.com%2Freference%2Fandroid%2Fos%2FAsyncTask.html%23onProgressUpdate%28Progress...%29), 调用了一次publishProgress(Progress...)之后,这个方法将在UI线程中被调用。执行的时间则未定义。这个方法被用于在后台计算仍在执行的过程中,在用户接口中显示任何形式的进度。比如,它可被用于在一个进度条中显示动画,或在一个text域中显示logs。

  4. [onPostExecute(Result)](https://www.oschina.net/action/GoToLink?url=http%3A%2F%2Fdeveloper.android.com%2Freference%2Fandroid%2Fos%2FAsyncTask.html%23onPostExecute%28Result%29),后台计算结束以后在UI线程中调用。后台计算的结果会作为一个参数传给这个步骤。

取消一个任务

可以在任何时间通过调用cancel(boolean)来取消一个任务。调用这个方法将导致后续对于isCancelled()的调用返回true。调用这个方法之后,在doInBackground(Object[])返回之后,则onCancelled(Object)将会被调用而不是onPostExecute(Object)。为了确保一个任务尽快被取消,只要可能(比如在一个循环中),你就应该总是在doInBackground(Object[])中周期性的检查isCancelled()的返回值。

线程规则

要使这个类能适当的运行,有一些线程规则必须遵循:

  • AsyncTask类必须在UI线程加载。自JELLY_BEAN起将自动完成。

  • task实例必须在UI线程创建。

  • [execute(Params...)](https://www.oschina.net/action/GoToLink?url=http%3A%2F%2Fdeveloper.android.com%2Freference%2Fandroid%2Fos%2FAsyncTask.html%23execute%28Params...%29) 必须在UI线程中调用。.

  • 不要手动地调用[onPreExecute()](https://www.oschina.net/action/GoToLink?url=http%3A%2F%2Fdeveloper.android.com%2Freference%2Fandroid%2Fos%2FAsyncTask.html%23onPreExecute%28%29)[onPostExecute(Result)](https://www.oschina.net/action/GoToLink?url=http%3A%2F%2Fdeveloper.android.com%2Freference%2Fandroid%2Fos%2FAsyncTask.html%23onPostExecute%28Result%29)[doInBackground(Params...)](https://www.oschina.net/action/GoToLink?url=http%3A%2F%2Fdeveloper.android.com%2Freference%2Fandroid%2Fos%2FAsyncTask.html%23doInBackground%28Params...%29)[onProgressUpdate(Progress...)](https://www.oschina.net/action/GoToLink?url=http%3A%2F%2Fdeveloper.android.com%2Freference%2Fandroid%2Fos%2FAsyncTask.html%23onProgressUpdate%28Progress...%29)

  • task只能被执行一次(如果尝试再次执行的话将会有一个exception跑出来)。

内存可观察性

AsyncTask保证所有的回调调用以这样的方式同步,下面的操作在没有显式同步的情况下也是安全的:

执行顺序

当第一次被引入时,AsyncTasks是在一个单独的后台线程中串行执行的。自[DONUT](https://www.oschina.net/action/GoToLink?url=http%3A%2F%2Fdeveloper.android.com%2Freference%2Fandroid%2Fos%2FBuild.VERSION_CODES.html%23DONUT),开始,这被改为了一个线程池,从而允许多个任务并行的执行。自HONEYCOMB开始,任务被执行在一个单独的线程上来避免并发执行所导致的常见的应用错误。

如果你真的想要并发执行,你可以以THREAD_POOL_EXECUTOR调用executeOnExecutor(java.util.concurrent.Executor, Object[])

AsyncTask的实现

上面AsyncTask的应用的部分,译自Google提供的关于AsyncTask的官方文档。关于AsnycTask在使用时候的注意事项,Google提供的文档可以说覆盖的已经是比较全面了。可是,我们在使用AsyncTask时,为什么一定要按照Google的文档中描述的那样来做呢?这就需要通过研究AsyncTask的实现来解答了。

任务的创建和启动

我们在利用AsnycTask的子类来执行异步任务时,所做的操作通常是new一个对象,执行它的execute()方法,然后就等着任务执行完成就可以了。但是,创建对象的过程,即任务的构造过程,又都做了些什么事情呢?我们的task又是如何被提交并执行起来的呢?这就需要来看下AsyncTask中,任务启动执行部分代码的实现了(android code的分析基于4.4.2_r1)。

先来看AsyncTask的构造,也就是任务的创建:

215    private final WorkerRunnable<Params, Result> mWorker;
216    private final FutureTask<Result> mFuture;
221    private final AtomicBoolean mTaskInvoked = new AtomicBoolean();
278    /**
279     * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
280     */
281    public AsyncTask() {
282        mWorker = new WorkerRunnable<Params, Result>() {
283            public Result call() throws Exception {
284                mTaskInvoked.set(true);
285
286                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
287                //noinspection unchecked
288                return postResult(doInBackground(mParams));
289            }
290        };
291
292        mFuture = new FutureTask<Result>(mWorker) {
293            @Override
294            protected void done() {
295                try {
296                    postResultIfNotInvoked(get());
297                } catch (InterruptedException e) {
298                    android.util.Log.w(LOG_TAG, e);
299                } catch (ExecutionException e) {
300                    throw new RuntimeException("An error occured while executing doInBackground()",
301                            e.getCause());
302                } catch (CancellationException e) {
303                    postResultIfNotInvoked(null);
304                }
305            }
306        };
307    }
308
309    private void postResultIfNotInvoked(Result result) {
310        final boolean wasTaskInvoked = mTaskInvoked.get();
311        if (!wasTaskInvoked) {
312            postResult(result);
313        }
314    }
315
316    private Result postResult(Result result) {
317        @SuppressWarnings("unchecked")
318        Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
319                new AsyncTaskResult<Result>(this, result));
320        message.sendToTarget();
321        return result;
322    }
654    private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
655        Params[] mParams;
656    }

可以看到这段code所做的事情。首先是创建了一个Callable,也就是上面的那个WorkerRunnable,其call()所做的事情正是调用我们实现的AsyncTask子类中所覆写的doInBackground()方法,并在其执行结束后将其执行的结果post出来。然后利用前面创建的Callable对象mWorker创建一个FutureTask。由此我们不难理解,AsyncTask的task,其本质是doInBackground(),但是是借助于FutureTask来表述的,以期能够方便的与执行框架衔接。

我们的task创建好了,那这个task又是如何提交并被启动执行的呢?我们接着来看execute()的实现:

218    private volatile Status mStatus = Status.PENDING;
506    /**
507     * Executes the task with the specified parameters. The task returns
508     * itself (this) so that the caller can keep a reference to it.
509     *
510     * <p>Note: this function schedules the task on a queue for a single background
511     * thread or pool of threads depending on the platform version.  When first
512     * introduced, AsyncTasks were executed serially on a single background thread.
513     * Starting with {@link android.os.Build.VERSION_CODES#DONUT}, this was changed
514     * to a pool of threads allowing multiple tasks to operate in parallel. Starting
515     * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, tasks are back to being
516     * executed on a single thread to avoid common application errors caused
517     * by parallel execution.  If you truly want parallel execution, you can use
518     * the {@link #executeOnExecutor} version of this method
519     * with {@link #THREAD_POOL_EXECUTOR}; however, see commentary there for warnings
520     * on its use.
521     *
522     * <p>This method must be invoked on the UI thread.
523     *
524     * @param params The parameters of the task.
525     *
526     * @return This instance of AsyncTask.
527     *
528     * @throws IllegalStateException If {@link #getStatus()} returns either
529     *         {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}.
530     *
531     * @see #executeOnExecutor(java.util.concurrent.Executor, Object[])
532     * @see #execute(Runnable)
533     */
534    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
535        return executeOnExecutor(sDefaultExecutor, params);
536    }
538    /**
539     * Executes the task with the specified parameters. The task returns
540     * itself (this) so that the caller can keep a reference to it.
541     *
542     * <p>This method is typically used with {@link #THREAD_POOL_EXECUTOR} to
543     * allow multiple tasks to run in parallel on a pool of threads managed by
544     * AsyncTask, however you can also use your own {@link Executor} for custom
545     * behavior.
546     *
547     * <p><em>Warning:</em> Allowing multiple tasks to run in parallel from
548     * a thread pool is generally <em>not</em> what one wants, because the order
549     * of their operation is not defined.  For example, if these tasks are used
550     * to modify any state in common (such as writing a file due to a button click),
551     * there are no guarantees on the order of the modifications.
552     * Without careful work it is possible in rare cases for the newer version
553     * of the data to be over-written by an older one, leading to obscure data
554     * loss and stability issues.  Such changes are best
555     * executed in serial; to guarantee such work is serialized regardless of
556     * platform version you can use this function with {@link #SERIAL_EXECUTOR}.
557     *
558     * <p>This method must be invoked on the UI thread.
559     *
560     * @param exec The executor to use.  {@link #THREAD_POOL_EXECUTOR} is available as a
561     *              convenient process-wide thread pool for tasks that are loosely coupled.
562     * @param params The parameters of the task.
563     *
564     * @return This instance of AsyncTask.
565     *
566     * @throws IllegalStateException If {@link #getStatus()} returns either
567     *         {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}.
568     *
569     * @see #execute(Object[])
570     */
571    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
572            Params... params) {
573        if (mStatus != Status.PENDING) {
574            switch (mStatus) {
575                case RUNNING:
576                    throw new IllegalStateException("Cannot execute task:"
577                            + " the task is already running.");
578                case FINISHED:
579                    throw new IllegalStateException("Cannot execute task:"
580                            + " the task has already been executed "
581                            + "(a task can be executed only once)");
582            }
583        }
584
585        mStatus = Status.RUNNING;
586
587        onPreExecute();
588
589        mWorker.mParams = params;
590        exec.execute(mFuture);
591
592        return this;
593    }

execute()是直接调用了executeOnExecutor(),参数为一个Executor和传进来的参数。而在executeOnExecutor()中,是首先检查了当前的这个AsyncTask的状态,如果不是Status.PENDING的话,则会抛出Exception出来,即是说,一个AsyncTask只能被执行一次。然后将状态设为Status.RUNNING。接着调用AsyncTask子类覆写的onPreExecute()方法。前面Google官方文档中4个步骤部分提到onPreExecute()这个步骤,这个方法在任务执行之前在UI线程中调用,这是由于我们在UI线程中执行execute()提交或启动任务时,会直接调用到这个方法(execute()->executeOnExecutor()->onPreExecute())。接着是将传进来的参数params赋给mWorker的mParams,至此才算是为任务的执行完全准备好了条件。万事俱备,只欠东风,准备好的任务只等提交执行了。接着便是将任务提交给传进来的Executor来执行。

那Executor又是如何来执行我们的Task的呢?我们来看AsyncTask中的Excutor。在execute()中是使用了sDefaultExecutor来执行我们的任务:

185    private static final ThreadFactory sThreadFactory = new ThreadFactory() {
186        private final AtomicInteger mCount = new AtomicInteger(1);
187
188        public Thread newThread(Runnable r) {
189            return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
190        }
191    };
192
193    private static final BlockingQueue<Runnable> sPoolWorkQueue =
194            new LinkedBlockingQueue<Runnable>(128);
195
196    /**
197     * An {@link Executor} that can be used to execute tasks in parallel.
198     */
199    public static final Executor THREAD_POOL_EXECUTOR
200            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
201                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
207    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
214    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
223    private static class SerialExecutor implements Executor {
224        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
225        Runnable mActive;
226
227        public synchronized void execute(final Runnable r) {
228            mTasks.offer(new Runnable() {
229                public void run() {
230                    try {
231                        r.run();
232                    } finally {
233                        scheduleNext();
234                    }
235                }
236            });
237            if (mActive == null) {
238                scheduleNext();
239            }
240        }
241
242        protected synchronized void scheduleNext() {
243            if ((mActive = mTasks.poll()) != null) {
244                THREAD_POOL_EXECUTOR.execute(mActive);
245            }
246        }
247    }

sDefaultExecutor在默认情况下是SERIAL_EXECUTOR,而后者则是一个SerialExecutor对象。在SerialExecutor的execute()中其实只是创建了一个匿名Runnable,也就是我们的task,并将它提交进一个任务队列mTasks中,如果mActive为null,则意味着这是进程所执行的第一个AsnycTask,则将会调用scheduleNext()来第一次将一个task提交进一个线程池executor,来在后台执行我们的任务。

Google的官方文档中提到,AsyncTask任务是串行执行的。AsyncTask任务的串行执行,不是指这些任务是在一个SingleThreadExecutor上执行的(其实任务是在一个可以执行并发操作的thread pool executor中执行的),只是这些任务提交给THREAD_POOL_EXECUTOR是串行的。只有当一个任务执行结束之后,才会有另外的一个任务被提交上去。这种任务的串行提交,是在SerialExecutor的execute()方法里那个匿名Runnable实现的,我们可以看到它是在执行完了一个task之后,才会去schedule另一个task的。

生产者线程可以看作是UI主线程,它会向任务队列中提交任务,而消费者则是执行了某个任务的THREAD_POOL_EXECUTOR中的某个线程。某个时刻,最多只有一个生产者,也最多只有一个消费者。

捋一捋我们的那个用户任务doInBackground()到此为止被包了多少层了。先是被包进一个Callable (mWorker : WorkerRunnable)中,mWorker又被包进了一个FutureTask (mFuture)中,而mFuture在这个地方则又被包进了一个匿名的Runnable中,然后我们的task才会真正的被提交给THREAD_POOL_EXECUTOR而等待执行。我们的doInBackground()执行时,其调用栈将有点类似于 doInBackground()<-mWorker.call()<-mFuture.run()<-匿名Runnable的run()<-...。

AsyncTask与UI线程的交互

AsyncTask可以很方便的与UI线程进行交互,它可以方便地向UI线程中publish任务执行的进度和任务执行的结果。但这种交互又是如何实现的呢?AsyncTask中有提到,在doInBackground()中,可以通过调用publishProgress(Progress...)来发布一个或多个的进度单元,而这些值将在UI线程中发布,在onProgressUpdate(Progress...)一步中。那我们就具体来看一下publishProgress()的实现:

212    private static final InternalHandler sHandler = new InternalHandler();
607    /**
608     * This method can be invoked from {@link #doInBackground} to
609     * publish updates on the UI thread while the background computation is
610     * still running. Each call to this method will trigger the execution of
611     * {@link #onProgressUpdate} on the UI thread.
612     *
613     * {@link #onProgressUpdate} will note be called if the task has been
614     * canceled.
615     *
616     * @param values The progress values to update the UI with.
617     *
618     * @see #onProgressUpdate
619     * @see #doInBackground
620     */
621    protected final void publishProgress(Progress... values) {
622        if (!isCancelled()) {
623            sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
624                    new AsyncTaskResult<Progress>(this, values)).sendToTarget();
625        }
626    }
637    private static class InternalHandler extends Handler {
638        @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
639        @Override
640        public void handleMessage(Message msg) {
641            AsyncTaskResult result = (AsyncTaskResult) msg.obj;
642            switch (msg.what) {
643                case MESSAGE_POST_RESULT:
644                    // There is only one result
645                    result.mTask.finish(result.mData[0]);
646                    break;
647                case MESSAGE_POST_PROGRESS:
648                    result.mTask.onProgressUpdate(result.mData);
649                    break;
650            }
651        }
652    }

所谓的与UI线程交互,其实也就是向一个Handler,即sHandler中发送消息。对于publishProgress(Progress...)的case,即是发送类型为MESSAGE_POST_PROGRESS的消息。随后消息会在UI线程中被处理,从而实现了将信息由后台线程传递至前台线程的目的。此处的sHandler是一个static的InternalHandler,也即是说,当AsyncTask类被加载起来的时候,这个对象就会被创建。我们知道,Handler其实是向一个Looper中发送消息。这个Looper隶属于某一个Looper thread,比如UI线程。我们可以在创建一个Handler时为它指定一个Looper,当然也可以不指定。如果不指定的话,那Handler关联的Looper就会是创建Handler的线程的Looper。由此则我们不难理解,为什麽Google的文档会强调,AsyncTask类必须在UI线程加载,因为sHandler所关联的Looper将会是VM中第一次访问AsyncTask的线程的Looper。也即是说,VM中第一个访问AsyncTask的线程,也将决定后续创建的所有的AsyncTask,它发布进度及结果的目标线程。实际上,由于后台线程与之交互的线程和后台线程通信是通过handler来完成的,则大概只要保证加载AsyncTask的线程是一个HandlerThread就可以了,只不过在这种情况下,创建的所有的AsyncTask都将会把进度和结果publish到那个HandlerThread中了。

不过话说回来了,是否真的有可能AsyncTask与之交互的线程只是一个普通的HandlerThread而不是main UI线程呢?答案当然是否定的。来看下面的这么一段code:

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
        AsyncTask.init();

这段code来自于frameworks/base/core/java/android/app/ActivityThread.java的main()方法中。可见Google也是早就料到了其中的风险,并已经有在此做防范,以确保AsyncTask中的static的Handler一定是在主线程创建的。

由InternalHandler.handleMessage()中对于MESSAGE_POST_PROGRESS,相信我们也就不难理解所谓的步骤onProgressUpdate(Progress...)是什么了。

看完了后台线程向UI线程发布进度的实现之后,我们再来看后台线程向UI线程中发布结果的实现:

316    private Result postResult(Result result) {
317        @SuppressWarnings("unchecked")
318        Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
319                new AsyncTaskResult<Result>(this, result));
320        message.sendToTarget();
321        return result;
322    }

同样是向sHandler中send message,只不过message的类型为了MESSAGE_POST_RESULT。这段code也不需要做过多解释。在MESSAGE_POST_RESULT的处理部分,我们看到了4个步骤中的另一个步骤onPostExecute():

628    private void finish(Result result) {
629        if (isCancelled()) {
630            onCancelled(result);
631        } else {
632            onPostExecute(result);
633        }
634        mStatus = Status.FINISHED;
635    }

也就是在任务结束之后,通知UI线程的部分。

并行执行AsyncTask及THREAD_POOL_EXECUTOR

AsyncTask不只能像上面说的那样串行执行,它们同样可以并行执行。即以THREAD_POOL_EXECUTOR为参数调用executeOnExecutor(java.util.concurrent.Executor, Object[])。比如:

new DownloadFilesTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, url1, url2, url3);

那THREAD_POOL_EXECUTOR本身又都有些什么样的限制呢?接下来我们来看THREAD_POOL_EXECUTOR的创建:

180    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
181    private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
182    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
183    private static final int KEEP_ALIVE = 1;
184
185    private static final ThreadFactory sThreadFactory = new ThreadFactory() {
186        private final AtomicInteger mCount = new AtomicInteger(1);
187
188        public Thread newThread(Runnable r) {
189            return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
190        }
191    };
192
193    private static final BlockingQueue<Runnable> sPoolWorkQueue =
194            new LinkedBlockingQueue<Runnable>(128);
195
196    /**
197     * An {@link Executor} that can be used to execute tasks in parallel.
198     */
199    public static final Executor THREAD_POOL_EXECUTOR
200            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
201                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

创建ThreadPoolExecutor传进去了一个容量为128的链接阻塞队列(sPoolWorkQueue)作为任务队列,这意味着在同一时间可以提交的最大的任务数量为128,既包括通过AsyncTask.execute()提交的任务,也包括通过AsyncTask.executeOnExecutor()提交的任务。线程池中线程的最小数量及最大数量则是与CPU数量相关的值CORE_POOL_SIZE和MAXIMUM_POOL_SIZE。KEEP_ALIVE则表示,当线程池中的线程数量超过了CORE_POOL_SIZE,那些空闲的线程能够存活的最长时间。sThreadFactory则用于维护线程池创建过的线程的数量,并给每个AsyncTask的线程创建一个适当的线程名。

Task的生命周期管理

声明周期管理,主要是指取消操作。

220    private final AtomicBoolean mCancelled = new AtomicBoolean();
423    /**
424     * Returns <tt>true</tt> if this task was cancelled before it completed
425     * normally. If you are calling {@link #cancel(boolean)} on the task,
426     * the value returned by this method should be checked periodically from
427     * {@link #doInBackground(Object[])} to end the task as soon as possible.
428     *
429     * @return <tt>true</tt> if task was cancelled before it completed
430     *
431     * @see #cancel(boolean)
432     */
433    public final boolean isCancelled() {
434        return mCancelled.get();
435    }
466    public final boolean cancel(boolean mayInterruptIfRunning) {
467        mCancelled.set(true);
468        return mFuture.cancel(mayInterruptIfRunning);
469    }

AsyncTask用一个原子变量来描述,任务是否是被cancel的。而提交给线程池的任务的cancel则委托给FutureTask。我们知道,在Java中,对于线程的cancel只能是提示性的,而不是强制性的。要真正的使任务有好的生命周期管理,则还需要在doInBackground()中,在适当的时机检查任务是否被cancel掉,并进一步清理环境,退出任务执行。

Done。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
11个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这