读书不宜拖沓
0. 背景
Android应用的主线程为ActivityThread,在第(10)章已经讲过,它主要负责处理界面事件,所以开发者应该避免在主线程中处理耗时的任务。为了减轻主线程的负担,开发者应该启用多线程来处理耗时的任务。在Android中可以创建多种线程,有的线程可以有自己的消息循环,有的线程则可以向主线程发送消息来使得界面发生改变。
##1. 主线程的消息循环
应用程序的主线程创建过程如下:ActivityManagerService线程请求Zygote进程创建应用进程;Zygote通过fork来创建一个新进程,新进程将ActivityThread的main作为入口进入Looper循环;Looper会调用静态成员函数prepareMainLooper创建一个Looper对象,并且在该应用进程中,该方法只会调用一次,因为只有一个主线程。
2. 子线程HandlerThread
Android应用中,创建子线程可以和java桌面应用一样,创建一个Thread子类,实现run函数,然后start即可。但是这种方式是没有消息循环的,是一种比较简单的方法。
另一种则是使用Android定制版的Thread–HandlerThread。HanlderThre ad的实现也很简单,完全可以模仿它实现一个自己的带有消息循环的Thread,或者实现一个集成HandlerThread的子类。
frameworks/base/core/java/android/os/HandlerThread.java :
然后创建一个实现了Runnable接口的类。
|
|
创建一个Runnable的实例对象,然后丢给HandleThread来处理。
|
|
在消息队列处理到该任务时,threadTask的run函数就会被调用执行。
3. 异步任务AsyncTask
一个AsyncTask的例子:
子线程如果想向主线程发送消息,则需要通过主线程的Handler。这里AsyncTask运行于子线程,但是无需Handler也可以向主线程发送消息。AsyncTask类的具体实现如下。
frameworks/base/core/java/android/os/AsyncTask.java :
sThreadFactory,sPoolWorkQueue和THREAD_POOL_EXECUTOR都是全局静态变量。在一个App进程中,因此,所有使用AsyncTask的异步线程使用的是同一个线程池,这样可以避免创建多个线程池占用资源。
ThreadPoolExecutor在执行任务时,会根据实际情况选择是否创建线程,具体如下:
- 线程池中的线程数量小于核心线程数,则会创建新的线程来执行新添加的任务。
- 如果大于核心线程数,小于最大线程数,则
- sPoolWorkQueue未满,则将新任务保存在队列中
- 如果已满,则创建一个新的线程来执行新的任务
- 如果线程池中线程数已经大于/等于最大线程数,如果队列不满,则放入队列;如果已满,则拒绝新任务。
InternalHandler类的实例对象sHandler是创建AsyncTask的线程的Handler,这里假设是主线程。
frameworks/base/core/java/android/os/AsyncTask.java :
其中result是一个AsyncTaskResult,里面保存了结果信息和其对应的AsyncTask。
frameworks/base/core/java/android/os/AsyncTask.java :
WorkerRunnable类的实例mWorker实现如下,保存了异步任务的输入数据:
frameworks/base/core/java/android/os/AsyncTask.java :
了解了AsyncTask的成员变量后,下面看一个异步任务创建的完整过程。
3.1 AsyncTask的构造函数
frameworks/base/core/java/android/os/AsyncTask.java :
SerialExecutor执行器的执行过程。
frameworks/base/core/java/android/os/AsyncTask.java :
到这里已经将任务交给线程池来启动,用户定义的doInBackground已经开始运行。
3.3 异步线程发送消息
异步线程可以通过publishProgress发送消息,具体实现如下:
主线程通过sHandler的handleMessage函数来处理该消息,然后调用用户自己实现的onProgressUpdate函数来实现UI的更新。
3.4 异步任务结束
在异步线程执行完任务后,会调用FutureTask的done函数来处理结束事件。这里主要处理的就是异步任务处理完成后,最后的返回值。最后的结果通过如下函数发送给主线程。
|
|
前面已经讲过,主线程会调用handler里的result.mTask.finish(result.mData[0])来处理结果。finish函数实现如下:
|
|
4. 总结
这里主要介绍了android应用里的几种线程的运行机制。
普通java线程Thread是最简单的一种方法,但是比较难管理,与主线程通信主要靠handler。而且,该中线程只能向主线程发消息,而主线程无法向子线程发消息。
HandlerThread则是可以有自己的looper和消息队列,将任务发送入消息队列来实现异步任务的处理,同时还方便管理。主线程和子线程可以通过对方的Handler相互发送消息。
AsyncTask是比较适用于Android应用的一种异步任务实现方式。虽然归根结底还是通过handler来向主进程发消息,但是整个异步任务执行过程的划分和管理更为科学,屏蔽了内部实现的复杂,为用户提供了更为简单实用的接口。