AsyncTask
,相信你们已经很熟悉了。它的内部封装了Thread
和Handler
,这让咱们能够将一些耗时操做放到AsyncTask
,而且能将结果及时更新到UI上。AsyncTask
主要用于短期耗时操做,长时间耗时操做不建议使用AsyncTask
。下面经过Google官方的一个例子来认识AsyncTask
的用法。bash
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> { protected void onPreExecute() { showProgress(); } 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"); } } 复制代码
AsyncTask
是一个抽象类,咱们要使用时必须自定义一个类继承于它。AsyncTask
的原型为:markdown
public abstract class AsyncTask<Params, Progress, Result> {
}
复制代码
它接收三个泛型参数,分别表示参数类型、进度类型、结果类型。多线程
上述的例子中DownloadFilesTask
接收参数类型为URL
类型,使用Integer
类型表示任务进度,最终的任务结果是一个Long
类型。并发
注意:上面三个泛型类型不必定都得用一个明确的类型,对于没有使用的类型,可使用
Void
类型代替。ide
继承AsyncTask
至少须要重写doInBackground
方法,同时AsyncTask
也提供了另外三个方法供咱们重写,分别是onPreExecute
、onProgressUpdate
、onPostExecute
。源码分析
publishProgress
方法被调用后执行,它运行在UI线程中。一般用于展现整个任务的一个进度。doInBackground
的返回结果会透传给onPostExecute
的参数值,它运行在主线程中。一般咱们从这里获取任务执行完成后的结果数据。AsyncTask
类必须在UI线程加载。(在4.1系统版本以上会自动完成)AsyncTask
对象必须在UI线程建立,也就是说AsyncTask
的构造方法必须在UI线程中调用。(通过测试AsyncTask
对象能够在子线程建立,只要保证execute
方法在UI线程执行就OK的。可是没有人会这样作,由于画蛇添足!!!)execute
方法必须在UI线程中调用。这样作是保证onPreExecute
方法运行在UI线程。onPreExecute
、doInBackground
、onProgressUpdate
、onProgressUpdate
方法。在AsyncTask
诞生之初,任务是在一个后台线程中顺序执行的。从Android 1.6开始,就变成了能够在后台线程中并行执行任务。而后,到了Android 3.0版本,又改为了单线程顺序执行,以此避免并发任务产生的错误行为。测试
为了验证上述结论,下面看一个Demo例子。this
public class MainActivity extends Activity { public static final String TAG = "MyApplication"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); new MyTask("task1").execute(); new MyTask("task2").execute(); new MyTask("task3").execute(); new MyTask("task4").execute(); new MyTask("task5").execute(); new MyTask("task6").execute(); } private class MyTask extends AsyncTask<Void, Void, Void> { private String taskName; MyTask(String taskName) { this.taskName = taskName; } @Override protected Void doInBackground(Void... integers) { try { Thread.sleep(6000); } catch (InterruptedException e) { e.printStackTrace(); } return null; } @Override protected void onPostExecute(Void aVoid) { SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Log.e(TAG, taskName + " finish at: " + df.format(new Date())); } } } 复制代码
这个例子比较简单,就是在MainActivity
启动时,执行了六次MyTask
,并将任务执行后的时间节点打印出来。url
手机的系统版本是Android 8.0,从上面的Log信息能够看出,AsyncTask
的确是串行执行的。因为现有测试机最低系统版本都是Android 4.1,已经很难找到Android 3.0如下的老古董机子了😓,因此咱们只能经过源码去验证Android 1.6到Android 3.0期间,AsyncTask
是不是并行执行的。spa
AsyncTask
是否串行或者并行执行,取决于它的execute
方法。
public final AsyncTask<Params, Progress, Result> execute(Params... params) { ...省略 mWorker.mParams = params; sExecutor.execute(mFuture); return this; } 复制代码
而execute
方法中经过sExecutor
,实际为ThreadPoolExecutor
对象,它的初始化以下所示。
private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,
MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory);
复制代码
ThreadPoolExecutor
是一个多线程容器,其中能够建立多个线程来执行多个任务。由此验证了Android 1.6版本到Android 3.0版本直接,AsyncTask
执行任务的机制的确也如今的机制不同,它可让任务并行执行。
咱们对比一下Android 8.0版本的execute
方法。
public final AsyncTask<Params, Progress, Result> execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); } public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec, Params... params) { ...省略 mWorker.mParams = params; exec.execute(mFuture); return this; } 复制代码
execute
方法中调用了executeOnExecutor
方法,并将sDefaultExecutor
做为Executor
对象传递进去,sDefaultExecutor
的初始化以下所示。
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR; public static final Executor SERIAL_EXECUTOR = new SerialExecutor(); private static class SerialExecutor implements Executor { final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>(); Runnable mActive; public synchronized void execute(final Runnable r) { mTasks.offer(new Runnable() { public void run() { try { r.run(); } finally { scheduleNext(); //任务执行完毕后继续执行scheduleNext方法 } } }); if (mActive == null) { //第一个任务会执行该方法 scheduleNext(); } } protected synchronized void scheduleNext() { if ((mActive = mTasks.poll()) != null) { //判断mTask队列中是否有下一个任务,有则取出来执行 THREAD_POOL_EXECUTOR.execute(mActive); } } } 复制代码
能够看到,在Android 8.0版本中,建立了一个ArrayDeque
队列,每次只从队列中获取一个任务执行,执行完毕后会继续判断队列中是否有任务,若是有则取出来执行,直到全部任务执行完毕为止。因而可知,Android 8.0版本执行任务是串行执行的。
若是咱们想改变AsyncTask
这种默认行为呢,能够修改么?答案是确定的。
咱们能够直接调用AsyncTask
的executeOnExecutor
方法,并将一个Executor
对象传递过去,就能变成并行的执行方法了。
对于上面的例子,能够这样改动。
public class MainActivity extends Activity { public static final String TAG = "MyApplication"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); new MyTask("task1").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); new MyTask("task2").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); new MyTask("task3").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); new MyTask("task4").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); new MyTask("task5").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); new MyTask("task6").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } private class MyTask extends AsyncTask<Void, Void, Void> { private String taskName; MyTask(String taskName) { this.taskName = taskName; } @Override protected Void doInBackground(Void... integers) { try { Thread.sleep(6000); } catch (InterruptedException e) { e.printStackTrace(); } return null; } @Override protected void onPostExecute(Void aVoid) { SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Log.e(TAG, taskName + " finish at: " + df.format(new Date())); } } } 复制代码
执行后,打印出来的Log信息以下图所示。
注意:这里前五个Task是同时执行的,由于AsyncTask.THREAD_POOL_EXECUTOR建立了五个核心线程,第六个任务须要等待空闲线程才能继续执行。因此会出现第六个任务和前五个任务执行时间不一致的现象,特此说明。