Android线程与消息机制
OUTLINE
§UI线程
§Looper
§消息机制
§线程交互
§AsyncTask
§Activity/Service与主线程
UI线程
先从一个经典错误开始:
android.view.ViewRootImpl$CalledFromWrongThreadException:
Only the original thread that created a view hierarchy can touch its views
为什么会出现这个错误?
UI的呈现必须在同一个线程里面完成。
试想,如果多个线程可以绘制UI,那么肯定乱套,呈现结果不可预期。
因此界面程序必然有一个UI线程,android,java,windows等都是如此。
Android UI 丈量、排布、绘制最终都是在ViewRootImpl里面完成的。
ViewRootImpl在执行UI操作之前,会进行线程检查。如果当前线程不是UI线程,就会抛出上述异常。
每个应用都对应一个进程,进程创建是伴随一个主线程创建。这个主线程就是UI线程。
Android的主线线程的入口在ActivityThread。ActivityThread和普通的java入口类一样,有一个静态main函数,作为主线程的入口。
ActivityThread与Android应用生命周期密切相关,后续会讲到。
Looper
Thread是一个线性执行,界面程序需要持续存在,因此需要一个循环,Looper就是Android里面线程循环的封装。
这样说还是比较抽象,那么Looper到底是什么?
消息机制
任务的循环执行,需要一个队列,可进可出,Android使用消息队列MessageQueue来实现。
一个Looper绑定一个Thread,在这个线程中循环;同时绑定一个MessageQueue,在这个消息队列中存取消息;然后,通过Handler向外接口。通过Handler把消息加入。
MessageQueue, Looper调用loop进行循环,循环地从消息队列中获取消息,处理消息。
Message
§消息出队:MessageQueue::next
§消息入队<- 生成消息:绑定Handler
Handler::obtainMessage
Message::obtainMessage(Handler)
消息队列中的消息是供Looper来消耗的,Looper通过MessageQueue的next方法取出消息。这个过程是在Looper内部完成,我们不需要太过关心。
不过next方法也是比较讲究的,这个方法最终会调用native的方法,可能会等待睡眠,直到IO事件或者消息入队把它唤醒。
Android的Message必须绑定到一个Handler,由这个Handler来发送和处理消息。消息入队的时候会检查消息是否绑定了Handler,如果没有绑定,会直接抛出异常。
因此我们通常是Handler::obtainMessage,这个方法获得的Message直接绑定到了Handler;另外,也会Message::obtainMessage,当前必须传递Handler。
Looper的创建
§Looper.prepare生成Looper实例
§ThreadLocal映射,将Looper和Thread绑定
Looper.prepare();
Looper looper = Looper.myLooper();
...
Looper.loop();
在线程中调用Looper.prepare完成Looper的创建。
通过ThreadLocal映射,Looper与线程绑定。
Looper创建时生成了MessageQueue。
Thread和ThreadLocal都是java的东西,Looper是Android的。Thread和Looper之前的绑定使用了ThreadLocal。ThreadLocal就是线程的存储器,它是一个通用设计,它为每个线程存储数据,从每个线程进来看到对应的线程的数据。因此Android的Looper很好地利用了这一点,使用ThreadLocal,从每个线程进来看到的Looper都是绑定的Looper。
MainLooper的创建
§ActivityThread main入口
Looper. prepareMainLooper();
...
Looper.loop();
主线程入口处(ActivityThread的main入口),调用Looper.prepareMainLooper,完成MainLooper的创建。再调用loop让主线程循环起来。
MainLooper作为静态变量保存在Looper中,可通过getMainLooper获取。
�Handler的创建
§Handler——Looper的对外接口
§Handler创建 <- Looper实例
−主线程Handler:直接获取MainLooper来创建。
−其他线程Handler:必须在调用Looper.prepare之后创建。
eg:
HandlerThread::onLooperPrepared
−不指定Looper,使用当前线程Looper。
Handler创建需要指定Looper,因此Handler的创建需要在调用Looper.prepare之后(HandlerThread.onLooperPrepared),否则会报异常。
如果没有Looper,默认使用当前线程绑定的Looper。
因此,通常在主线程可以任意创建Handler,因为MainLooper在主线程启动时已经prepare。
而在其他线程创建Handler时需要先调用Looper.prepare。
非主线线程Handler典型创建方法是通过HandlerThread。HandlerThread是Android联结Handler和线程的封装,它用onLooperPrepared回调提供给外界创建Handler。
线程的创建
§线性执行:直接或间接new Thread
§循环:
1. 基于Looper的Thread ->HandlerThread
2. 自己为线程实现循环机制
线程交互
线程之间的交互通过消息机制完成。具体来说,A线程需要发送消息到B线程,需要通过持有B线程Looper的Handler发送消息。
eg: A线程需要操作B线程
A发送消息给B
−A发送:A需要Handler实例
−给B:Handler实例必须持有B线程的Looper。
实现:
−B线程生成Handler
−Handler定义消息和消息处理
主线程与非主线程交互
§主线程执行非主线程操作
−获取非主线程Handler发送消息。 针对持久存在的非主线程处理
−通过AsyncTask::doInBackground执行。 针对临时存在的后台线程处理
-View.post(Runnable)。 在View中执行。
-Activity.runOnUiThread。 在Activity中执行。
§非主线程执行主线程操作
−获取MainLooper生成主线程Handler。 针对持久的主线程处理
eg:ViewRootHander,Activity的Handler
−通过AsyncTask::onProgressUpdate或onPostExecute。 针对临时主线程处理
−通过临时new Handler(传递MainLooper)来post执行。
View的post函数将Runnable加入到ViewRootImpl的执行队列中,在下次ViewRootImpl执行tranversal时,将队列任务加入一个主线程的Handler的消息队列。
Activity的runOnUiThread后文再详细讲解。
AsyncTask是Android对主线程和后台线程处理的一个封装,它是线性执行的,不作循环。
AsyncTask
§主线程执行——持有MainLooper的Handler静态成员sHandler,在主线程入口处初始化。
§后台执行——新建线程,设置为优先级:
THREAD_PRIORITY_BACKGROUND
onProgressUpdate
onPostExecute 主线程
doInBackground 后台线程
onPreExecute 取决于调用线程
AsyncTask持有一个静态Handler,由主线程入口处初始化创建,因此它持有MainLooper。onProgressUpdate和onPostExecute是通过该Handler执行的,因此都是主线程操作。
类的静态成员和静态块,是在类加载的时候执行的。在主线程入口的地方,没有必须创建一个AsyncTask实例,但是需要为它初始Handler,因此调用AsyncTask一个无用的静态方法init,仅仅是为了初始化Handler。
AsyncTask的后台执行是通过创建一个新的线程,非设置线程优先级为Background,因此它执行后台操作。
onProgressUpdate和onPostExecute是通过该Handler执行的,因此都是主线程操作。doInBackground是新建线程(THREAD_PRIORITY_BACKGROUND优先级),因此是后台线程操作。onPreExecute在execute中执行,所在线程取决于调用的线程。
线程优先级
§设置线程优先级
−android.os.Process.setThreadPriority:[-20, 19]。越小优先级越高
−java.lang.Thread.setPriority:[1, 10]。越大优先级越高。
§THREAD_PRIORITY_BACKGROUND(10):标准的后台优先级
§默认线程优先级:THREAD_PRIORITY_DEFAULT(0),中等
设置线程优先级有两种方式:
android.os.Process.setThreadPriority:[-20,19]。越小优先级越高
java.lang.Thread.setPriority:[1, 10]。越大优先级越高。
优先级调度是底层实现的,没有具体深入。优先级的设置和执行结果是没有办法准确预期的,但是可以肯定的是Process.setThreadPriority的效果更符合预期。
Activity/Service与主线程
§Activity和Service运行在主线程(ActivityThread$H)
onCreate,onResume,…
§Activity Handler
与AsyncTask类似,Activity内部有一个持有MainLooper的Handler。
Acitivty的启动过程是通过ActivityThread.H这样一个Handler来调用的,这是一个主线程的Handler,因此主线程的启动都在主线程。
另外,与AsyncTask类似,Activity内部有一个持有MainLooper的Handler。因此Activity提供了一个runOnUiThread的方法,方便直接执行UI操作。