[02] AsyncTask
链接:http://developer.android.com/reference/android/os/AsyncTask.html
2.1 Usage
2.2 Threading rules
2.3 Memory observability
2.4 Order of execution
2.5 源码分析
2.6 小结
AsyncTask enables proper and easy use of the UI thread. This class allows to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers.
AsyncTask is designed to be a helper class around Thread
and Handler
and does not constitute a generic threading framework. AsyncTasks should ideally be used for short operations (a few seconds at the most.) If you need to keep threads running for long periods of time, it is highly recommended you use the various APIs provided by thejava.util.concurrent
pacakge such as Executor
, ThreadPoolExecutor
and FutureTask
.
翻译:AsyncTask使得开发者可以快捷方便地操作UI线程。它可以在后台运行并将结果返回给UI线程,在这个过程中我们不需要人为的调用 Thread、Handler对象。AsyncTask作为一个 Thread和 Handler的帮助类,并不创建一个通用线程框架(?)。 AsyncTasks应该用来进行一些短时间内的耗时操作,如果长时间内需要保持一个后台线程的运行,应该使用 Exector、ThreadPoolExecutor、FutureTask这些 API。
收获:按照描述来看,AsyncTask极大的简化了开发者的工作,在内部执行了新线程的创建并有直接接口能和UI线程进行通信。封装的这么完整的类肯定有很大的缺点,否则所有涉及多线程的实现都应该使用这种 AsyncTask了,这次学习的内容就是找到这些缺点,与直接使用 Thread + Handler的方法比较优劣。
2.1 Usage
因为 AsyncTask是 abstract类型的,所以要使用一个子类继承 AsyncTask并至少重写其中的一个doInBackground(Params...)方法:
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");
}
}
如果三个参数中的某一个或者几个用不到的话,应该使用 Void类型。
使用 AsyncTask的时候只需要实例化一个类的对象并调用 execute(Params... params)方法就可以了:
execute(Params... params)
在执行了 execute方法之后, AsyncTask内部方法的调用顺序依次是 onPreExecute() -- doInBackground(Params... params) -- onPostExecute(Result result),其中 doInBackground(Params... params)方法是执行在非UI线程中的。
调用 publishProgress(Progress... values)方法会触发 onProgressUpdate(Progress... values),调用 cancel(boolean mayInterruptIfRunning)会触发 onCancelled(Result result)。
2.2 Threading Rules
调用 cancel(boolean mayInterruptIfRunning)方法之后,isCanncelled()方法返回值变为 true,同时 doInBackground结束之后也不会回调 onPostExecute(Result result)方法,而是会调用 onCancelled(Result result)。
需要注意的一点是,如果在 doInBackground方法中没有及时的检查当前 task是否已经处于 cancelled状态,那么在 cancel被调用之后,task并不会立即停止,而是会执行完 doInBackground方法。
这里应该算是 AsyncTask在使用中的第一个限制,如果在 doInBackground方法中没有循环操作的代码,task也就不能被简单的调用 cancel方法中断。
2.3 Memory observability
AsyncTask保证所有的回调都是使用 synchronized加过锁的,所以即便以下操作没有使用 synchronized关键字,它们也都是线程安全的:
1. 在 AsyncTask的构造方法或是 onPreExecute()方法中设置成员变量,并把值传给 doInBackground(Params... params);
2. 在 doInBackground(Params... params)方法中设置成员变量,并把值传给 onProgressUpdate(Progress...) 和 onPostExecute(Result)。
我们都知道使用 synchronized关键字会影响代码执行效率,需要参照源码具体分析是否因为在封装方法的过程中不能控制 synchronized关键字的作用范围导致一些不必要的耗时。
2.4 Order of execution
When first introduced, AsyncTasks were executed serially on a single background thread. Starting with android.os.Build.VERSION_CODES.DONUT
, this was changed to a pool of threads allowing multiple tasks to operate in parallel. Starting with android.os.Build.VERSION_CODES.HONEYCOMB
, tasks are executed on a single thread to avoid common application errors caused by parallel execution.
一开始设计 AsyncTask的时候,所有的 tasks都是在单一的后台线程中顺序执行的。从 Android1.6开始,改为由一个线程池并发执行多个 tasks。从 Android3.0开始,又改为由单个线程执行 tasks,这个改动是为了避免产生一些由并发引起的系统错误。
如果开发者仍然希望并发执行多个 task,可以调用 executeOnExecutor(java.util.concurrent.Executor, Object[]),并使用 THREAD_POOL_EXECUTOR来达到目的。
这里应该就是 AsyncTask最大的缺点,在 Android应用中,很多场景需要大量使用线程执行一些耗时操作,例如需要从网络获取图片的相册、新闻列表、微博列表等实现。
2.5 源码分析
· 采用 AtomicInteger等原子化对象保障多线程安全;
· 采用 BlockingQueue + Executor + FutureTask执行任务队列;
· 设置优先级为 Process.THREAD_PRIORITY_BACKGROUND,略低于一般进程。
在 Processes and Threads中,介绍了Android进程的几种优先级,从高到低依次是:进程含有与用户直接交互的 Activity&与之绑定的 Service > 进程含有可见的 Activity&与之绑定的 Service > 进程含有后台 Service > 进程含有不可见的 Activity > 空进程。并且为其他进程提供服务的进程,其优先级不低于所服务的对象。
2.6 小结
在看文档之前已经初步决定要使用 AsyncTask去做部分功能了,看过文档和源码之后才发现使用限制还是比较多的,特别是不能即时打断和更新 UI,再加上产品需要考虑多个 Android版本,所以还是放弃了。之前看过一些网友仿照 AsyncTask源码写的类,主要改动都是使用并发执行器、调高线程池上线、调高最多同时执行的线程数目。还是自己用 Thread + Handler实现比较好。