Android AsyncTask实现

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))。

这里是一个继承的例子:

1 private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> { 2     protected Long doInBackground(URL... urls) { 3         int count = urls.length; 4         long totalSize = 0; 5         for (int i = 0; i < count; i++) { 6             totalSize += Downloader.downloadFile(urls[i]); 7             publishProgress((int) ((/ (float) count) * 100)); 8             // Escape early if cancel() is called 9             if (isCancelled()) break; 10         } 11         return totalSize; 12     } 13     protected void onProgressUpdate(Integer... progress) { 14         setProgressPercent(progress[0]); 15     } 16     protected void onPostExecute(Long result) { 17         showDialog("Downloaded " + result + " bytes"); 18     } 19 }

创建之后,一个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的构造,也就是任务的创建:

1215    private final WorkerRunnable<Params, Result> mWorker; 2216    private final FutureTask<Result> mFuture; 3221    private final AtomicBoolean mTaskInvoked = new AtomicBoolean(); 4278    /** 5279     * Creates a new asynchronous task. This constructor must be invoked on the UI thread. 6280     */ 7281    public AsyncTask() { 8282        mWorker = new WorkerRunnable<Params, Result>() { 9283            public Result call() throws Exception { 10284                mTaskInvoked.set(true); 11285 12286                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 13287                //noinspection unchecked 14288                return postResult(doInBackground(mParams)); 15289            } 16290        }; 17291 18292        mFuture = new FutureTask<Result>(mWorker) { 19293            @Override 20294            protected void done() { 21295                try { 22296                    postResultIfNotInvoked(get()); 23297                } catch (InterruptedException e) { 24298                    android.util.Log.w(LOG_TAG, e); 25299                } catch (ExecutionException e) { 26300                    throw new RuntimeException("An error occured while executing doInBackground()", 27301                            e.getCause()); 28302                } catch (CancellationException e) { 29303                    postResultIfNotInvoked(null); 30304                } 31305            } 32306        }; 33307    } 34308 35309    private void postResultIfNotInvoked(Result result) { 36310        final boolean wasTaskInvoked = mTaskInvoked.get(); 37311        if (!wasTaskInvoked) { 38312            postResult(result); 39313        } 40314    } 41315 42316    private Result postResult(Result result) { 43317        @SuppressWarnings("unchecked") 44318        Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT, 45319                new AsyncTaskResult<Result>(this, result)); 46320        message.sendToTarget(); 47321        return result; 48322    } 49654    private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> { 50655        Params[] mParams; 51656    }

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

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

1218    private volatile Status mStatus = Status.PENDING; 2506    /** 3507     * Executes the task with the specified parameters. The task returns 4508     * itself (this) so that the caller can keep a reference to it. 5509     * 6510     * <p>Note: this function schedules the task on a queue for a single background 7511     * thread or pool of threads depending on the platform version.  When first 8512     * introduced, AsyncTasks were executed serially on a single background thread. 9513     * Starting with {@link android.os.Build.VERSION_CODES#DONUT}, this was changed 10514     * to a pool of threads allowing multiple tasks to operate in parallel. Starting 11515     * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, tasks are back to being 12516     * executed on a single thread to avoid common application errors caused 13517     * by parallel execution.  If you truly want parallel execution, you can use 14518     * the {@link #executeOnExecutor} version of this method 15519     * with {@link #THREAD_POOL_EXECUTOR}; however, see commentary there for warnings 16520     * on its use. 17521     * 18522     * <p>This method must be invoked on the UI thread. 19523     * 20524     * @param params The parameters of the task. 21525     * 22526     * @return This instance of AsyncTask. 23527     * 24528     * @throws IllegalStateException If {@link #getStatus()} returns either 25529     *         {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}. 26530     * 27531     * @see #executeOnExecutor(java.util.concurrent.Executor, Object[]) 28532     * @see #execute(Runnable) 29533     */ 30534    public final AsyncTask<Params, Progress, Result> execute(Params... params) { 31535        return executeOnExecutor(sDefaultExecutor, params); 32536    } 33538    /** 34539     * Executes the task with the specified parameters. The task returns 35540     * itself (this) so that the caller can keep a reference to it. 36541     * 37542     * <p>This method is typically used with {@link #THREAD_POOL_EXECUTOR} to 38543     * allow multiple tasks to run in parallel on a pool of threads managed by 39544     * AsyncTask, however you can also use your own {@link Executor} for custom 40545     * behavior. 41546     * 42547     * <p><em>Warning:</em> Allowing multiple tasks to run in parallel from 43548     * a thread pool is generally <em>not</em> what one wants, because the order 44549     * of their operation is not defined.  For example, if these tasks are used 45550     * to modify any state in common (such as writing a file due to a button click), 46551     * there are no guarantees on the order of the modifications. 47552     * Without careful work it is possible in rare cases for the newer version 48553     * of the data to be over-written by an older one, leading to obscure data 49554     * loss and stability issues.  Such changes are best 50555     * executed in serial; to guarantee such work is serialized regardless of 51556     * platform version you can use this function with {@link #SERIAL_EXECUTOR}. 52557     * 53558     * <p>This method must be invoked on the UI thread. 54559     * 55560     * @param exec The executor to use.  {@link #THREAD_POOL_EXECUTOR} is available as a 56561     *              convenient process-wide thread pool for tasks that are loosely coupled. 57562     * @param params The parameters of the task. 58563     * 59564     * @return This instance of AsyncTask. 60565     * 61566     * @throws IllegalStateException If {@link #getStatus()} returns either 62567     *         {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}. 63568     * 64569     * @see #execute(Object[]) 65570     */ 66571    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec, 67572            Params... params) { 68573        if (mStatus != Status.PENDING) { 69574            switch (mStatus) { 70575                case RUNNING: 71576                    throw new IllegalStateException("Cannot execute task:" 72577                            + " the task is already running."); 73578                case FINISHED: 74579                    throw new IllegalStateException("Cannot execute task:" 75580                            + " the task has already been executed " 76581                            + "(a task can be executed only once)"); 77582            } 78583        } 79584 80585        mStatus = Status.RUNNING; 81586 82587        onPreExecute(); 83588 84589        mWorker.mParams = params; 85590        exec.execute(mFuture); 86591 87592        return this; 88593    }

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来执行我们的任务:

1185    private static final ThreadFactory sThreadFactory = new ThreadFactory() { 2186        private final AtomicInteger mCount = new AtomicInteger(1); 3187 4188        public Thread newThread(Runnable r) { 5189            return new Thread(r, "AsyncTask #" + mCount.getAndIncrement()); 6190        } 7191    }; 8192 9193    private static final BlockingQueue<Runnable> sPoolWorkQueue = 10194            new LinkedBlockingQueue<Runnable>(128); 11195 12196    /** 13197     * An {@link Executor} that can be used to execute tasks in parallel. 14198     */ 15199    public static final Executor THREAD_POOL_EXECUTOR 16200            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, 17201                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory); 18207    public static final Executor SERIAL_EXECUTOR = new SerialExecutor(); 19214    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR; 20223    private static class SerialExecutor implements Executor { 21224        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>(); 22225        Runnable mActive; 23226 24227        public synchronized void execute(final Runnable r) { 25228            mTasks.offer(new Runnable() { 26229                public void run() { 27230                    try { 28231                        r.run(); 29232                    } finally { 30233                        scheduleNext(); 31234                    } 32235                } 33236            }); 34237            if (mActive == null) { 35238                scheduleNext(); 36239            } 37240        } 38241 39242        protected synchronized void scheduleNext() { 40243            if ((mActive = mTasks.poll()) != null) { 41244                THREAD_POOL_EXECUTOR.execute(mActive); 42245            } 43246        } 44247    }

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()的实现:

1212    private static final InternalHandler sHandler = new InternalHandler(); 2607    /** 3608     * This method can be invoked from {@link #doInBackground} to 4609     * publish updates on the UI thread while the background computation is 5610     * still running. Each call to this method will trigger the execution of 6611     * {@link #onProgressUpdate} on the UI thread. 7612     * 8613     * {@link #onProgressUpdate} will note be called if the task has been 9614     * canceled. 10615     * 11616     * @param values The progress values to update the UI with. 12617     * 13618     * @see #onProgressUpdate 14619     * @see #doInBackground 15620     */ 16621    protected final void publishProgress(Progress... values) { 17622        if (!isCancelled()) { 18623            sHandler.obtainMessage(MESSAGE_POST_PROGRESS, 19624                    new AsyncTaskResult<Progress>(this, values)).sendToTarget(); 20625        } 21626    } 22637    private static class InternalHandler extends Handler { 23638        @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"}) 24639        @Override 25640        public void handleMessage(Message msg) { 26641            AsyncTaskResult result = (AsyncTaskResult) msg.obj; 27642            switch (msg.what) { 28643                case MESSAGE_POST_RESULT: 29644                    // There is only one result 30645                    result.mTask.finish(result.mData[0]); 31646                    break; 32647                case MESSAGE_POST_PROGRESS: 33648                    result.mTask.onProgressUpdate(result.mData); 34649                    break; 35650            } 36651        } 37652    }

所谓的与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:

1        if (sMainThreadHandler == null) { 2            sMainThreadHandler = thread.getHandler(); 3        } 4        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线程中发布结果的实现:

1316    private Result postResult(Result result) { 2317        @SuppressWarnings("unchecked") 3318        Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT, 4319                new AsyncTaskResult<Result>(this, result)); 5320        message.sendToTarget(); 6321        return result; 7322    }

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

1628    private void finish(Result result) { 2629        if (isCancelled()) { 3630            onCancelled(result); 4631        } else { 5632            onPostExecute(result); 6633        } 7634        mStatus = Status.FINISHED; 8635    }

也就是在任务结束之后,通知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的创建:

1180    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); 2181    private static final int CORE_POOL_SIZE = CPU_COUNT + 1; 3182    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1; 4183    private static final int KEEP_ALIVE = 1; 5184 6185    private static final ThreadFactory sThreadFactory = new ThreadFactory() { 7186        private final AtomicInteger mCount = new AtomicInteger(1); 8187 9188        public Thread newThread(Runnable r) { 10189            return new Thread(r, "AsyncTask #" + mCount.getAndIncrement()); 11190        } 12191    }; 13192 14193    private static final BlockingQueue<Runnable> sPoolWorkQueue = 15194            new LinkedBlockingQueue<Runnable>(128); 16195 17196    /** 18197     * An {@link Executor} that can be used to execute tasks in parallel. 19198     */ 20199    public static final Executor THREAD_POOL_EXECUTOR 21200            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, 22201                    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的生命周期管理

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

1220    private final AtomicBoolean mCancelled = new AtomicBoolean(); 2423    /** 3424     * Returns <tt>true</tt> if this task was cancelled before it completed 4425     * normally. If you are calling {@link #cancel(boolean)} on the task, 5426     * the value returned by this method should be checked periodically from 6427     * {@link #doInBackground(Object[])} to end the task as soon as possible. 7428     * 8429     * @return <tt>true</tt> if task was cancelled before it completed 9430     * 10431     * @see #cancel(boolean) 11432     */ 12433    public final boolean isCancelled() { 13434        return mCancelled.get(); 14435    } 15466    public final boolean cancel(boolean mayInterruptIfRunning) { 16467        mCancelled.set(true); 17468        return mFuture.cancel(mayInterruptIfRunning); 18469    }

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

Done。

点赞
收藏

评论区

加载中...

相关推荐

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

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )

Android AsyncTask实现 - HelloWorld