前言
我们知道在android中每个Activity都有一个UI线程,在这里线程中主要就是初始化UI、更新UI、处理事件等。而更新UI的数据大多都是从网络上获取,但
进行网络操作都是在子线程中进行的,以此来避免ANR异常。在子线程是不能直接进行修改UI的,在《进程与线程》这篇文章中提到过修改UI元素有三种方式:
(1)使用runOnUiThread、post等方法;
(2)使用Handle机制;
(3)使用
异步任务AsyncTask;
本篇主要分析下
Handle机制工作的原理,
转载请注明出处:小石头的博客
http://blog.youkuaiyun.com/lu1024188315/article/details/74518599
一 消息机制的核心类
消息机制核心类有:Message、Handler、MessageQueue、Looper。
1 Message
从字面理解就是消息,它的作用就是储存信息,它是
线程间交流的信息,子线程从网络上获取数据,把数据存储在Message中,然后发送这个Message给UI线程进而就可以更新UI元素。
(1)
成员变量
它实现了Parcelable接口,它有几个重要的成员变量:
public final class Message implements Parcelable { //信息码用于区分Message public int what; //存储int 数据 public int arg1; //存储int 数据 public int arg2; //存储Object类型数据 例如从网络上获取的数据 public Object obj; ...... /*package*/ Handler target;//存储Handler对象 /*package*/ Runnable callback;//存储Runnable对象 ...... }
(2)获取实例
一般情况下获取一个对象的实例,首先会想到通过构造方法,没错Message也可以通过构造方法:
Message#构造方法:
public Message() { }
但这样会有个问题,在一个App中可能访问网络比较频繁,每次获取数据后,都通过new 实例化一个Message,这样将会大大增加了内存开销。那么我们应该怎么更好地获取这个对象的实例呢?当然是通过obtain方法:
Message#
obtain:
public static Message obtain() { synchronized (sPoolSync) { if (sPool != null) //sPool是Message类型,如果sPool不为null,那么直接使用它, //如果为null那么就通过构造方法new一个实例,其实这个方法本质就是一个单例模式 Message m = sPool; sPool = m.next; m.next = null; m.flags = 0; // clear in-use flag sPoolSize--; return m; } } return new Message(); } public static Message obtain(Handler h, Runnable callback) { Message m = obtain(); m.target = h; //保存callback实例到本地变量m.callback中,如果m.callback不为null在消息处理的过程中首先调用它进行处理Message m.callback = callback; return m; }
说明,当然还有其他
obtain方法拱使用,这里就不一一列出了,同时Handle中也有获取
Message实例的方法,这个方法也很常用:
Handle
#
obtainMessage:
public final Message obtainMessage(){ return Message.obtain(this); } public final Message obtainMessage(int what, Object obj){ return Message.obtain(this, what, obj); } public final Message obtainMessage(int what, int arg1, int arg2){ return Message.obtain(this, what, arg1, arg2); } public final Message obtainMessage(int what, int arg1, int arg2, Object obj){ return Message.obtain(this, what, arg1, arg2, obj); }
说明,其本质就是调用了
Message的
obtain方法。
2 Handler
Handler处理者,是Message的主要处理者,负责Message的发送,Message内容的执行处理。子线程就是通过Handler对象实例调用sendMessage(Message)发送信息。处理信息是在该类的 handleMessage(Message)方法,它是处理这些Message的操作内容,例如Update UI。通常需要子类化Handler来实现handleMessage方法。
(1)重要成员变量
mCallback:
Callback类型,作用就是保存构造函数传过来的
Callback对象引用,以便对
Message进行处理。
(2)构造函数
Handler#构成函数:
public Handler() { this(null, false); } public Handler(Callback callback) { this(callback, false); } public Handler(Looper looper) { this(looper, null, false); } public Handler(Looper looper, Callback callback) { this(looper, callback, false); } public Handler(boolean async) { this(null, async); } public Handler(Callback callback, boolean async) { ...... //获取当前线程的looper对象引用 mLooper = Looper.myLooper(); if (mLooper == null) {//如果mLooper为null 抛出异常 throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } //获取当前线程looper的消息队列 mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; } public Handler(Looper looper, Callback callback, boolean async) { mLooper = looper; //获取当前线程looper的消息队列 mQueue = looper.mQueue; mCallback = callback; mAsynchronous = async; }
(3)发送信息
Handler#
sendMessage:
public final boolean sendMessage(Message msg){ return sendMessageDelayed(msg, 0); }
说明,这里发送的是一个Message对象,其实它还可以发送一个Runnable对象,如下:
Handler#post
:
public final boolean post(Runnable r){ return sendMessageDelayed(getPostMessage(r), 0); }
说明,发送信息的详细流程下面具体分析。
(4)处理信息
Handler处理信息是在handleMessage方法中进行的,在使用的过程一般是重写Handler中handleMessage方法:
Handler#
handleMessage:
public void handleMessage(Message msg) { }
说明,
处理
信息的详细流程下面具体分析。
3 Looper
顾名思义它是循环着,它
是Handler和消息队列之间通讯的桥梁,
首先通过Handler发送的消息,会被传递给Looper,Looper把消息放入队列,然后
Looper
把消息队列里的消息广播给所有的
Handler,进而在
handleMessage方法中对消息进行处理。
Looper是每条线程里的Message Queue的管家。Android没有全局的Message Queue,而Android会自动替主线程(UI线程)建立Message Queue,但在子线程里并没有建立Message Queue。所以调用Looper.getMainLooper()得到的主线程的Looper不为NULL,但调用Looper.myLooper() 得到当前线程的Looper就有可能为NULL。每个线程中只能有一个
Looper对象引用
。
(1)重要的成员变量
mQueue
:MessageQueue类型,消息队列用于存储消息,它在Looper中创建的,创建过程如下:
Looper#prepare:
//初始化当前的线程为looper线程 public static void prepare() { prepare(true); }
Looper#构造函数:
private Looper(boolean quitAllowed) {//创建消息队列对象引用 mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
说明,在这里
MessageQueue与线程的Looper关联一起了,这是非常重要的一步,无论是把消息放置到
MessageQueue中,还是从
MessageQueue取消息都是在以这个关联的情况下进行的。
获取这个
MessageQueue对象引用有两种方式:
Looper#myQueue:
//通过Looper获取当前线程的消息队列 public static @NonNull MessageQueue myQueue() { return myLooper().mQueue; }
Looper#getQueue:
//获取当前线程的消息队列 public @NonNull MessageQueue getQueue() { return mQueue; }
(2)源码分析
public final class Looper { // 线程局部变量在《线程管理三(ThreadLocal)》中,ThreadLocal作用已经讲过 static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); //UI线程Looper对象 private static Looper sMainLooper; //线程队列 final MessageQueue mQueue; final Thread mThread; ...... //初始化当前的线程为looper线程 public static void prepare() { prepare(true); } //初始化当前的线程为looper线程,和上面的方法不同之处就是可以自己设置是否允许退出looper。 private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) {//一个线程中只能有一个looper对象引用
throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); } //初始化当前的UI线程为looper线程 public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) {//试图在UI线程再创建一个looper实例,抛出异常
throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } } //获取UI线程中的Looper对象引用,此对象引用一定不会为null public static Looper getMainLooper() { synchronized (Looper.class) { return sMainLooper; } } //循环处理消息队列 public static void loop() { final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } final MessageQueue queue = me.mQueue; // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); for (;;) {//遍历消息队列 Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } ....... try {//target就是handle类型,调用handle的dispatchMessage对消息进行处理 msg.target.dispatchMessage(msg); } finally { ....... } ......//回收资源 msg.recycleUnchecked(); } } //获取当前线程的looper对象引用,此对象引用可能为null public static @Nullable Looper myLooper() { return sThreadLocal.get(); } //通过Looper获取当前线程的消息队列 public static @NonNull MessageQueue myQueue() { return myLooper().mQueue; } private Looper(boolean quitAllowed) {//创建消息队列对象引用 mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); } //判断当前线程是否是looper线程 public boolean isCurrentThread() { return Thread.currentThread() == mThread; } ...... //退出循环 public void quit() { mQueue.quit(false); } //安全地退出循环 public void quitSafely() { mQueue.quit(true); } //获取当前环境下的线程 public @NonNull Thread getThread() { return mThread; } //获取当前线程的消息队列 public @NonNull MessageQueue getQueue() { return mQueue; } ...... }
4 MessageQueue
用来存放通过Handler发布的消息,通常附属于某一个创建它的线程,可以通过Looper.myQueue()得到当前线程的消息队列,
按照先进先出执行。
每个
MessageQueue
都会有一个对应的Handler。Handler会向
MessageQueue
通过两种方法发送消息:sendMessage或post。这两种消息都会插在
MessageQueue
队尾并按先进先出执行。但通过这两种方法发送的消息执行的方式略有不同:通过sendMessage发送的是一个message对象,会被 Handler的handleMessage()函数处理;而通过post方法发送的是一个runnable对象,则会自己执行。
(1)重要的成员变量
mMessages:这是一个非常重要的成员变量,它对我们理解Message是如何放置到消息队列中,和如何取出起到关键性的作用,它Message类型,
用于存储消息队列中队头的Message,遍历
消息队列以它为坐标开始的。
二 发送消息的流程
在上面已经提到了发送消息的方法,是通过调用
Handler的方法
sendMessage:
Handler#
sendMessage:
public final boolean sendMessage(Message msg){ return sendMessageDelayed(msg, 0); }
Handler#
sendMessageDelayed:
public final boolean sendMessageDelayed(Message msg, long delayMillis){ if (delayMillis < 0) {//延迟时间小于0时,让其默认值为0 delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); } }
Handler#
sendMessageAtTime:
public boolean sendMessageAtTime(Message msg, long uptimeMillis) { MessageQueue queue = mQueue;//mQueue就是刚刚提到的handle的重要成员变量,这个变量是在looper中实例化的 if (queue == null) {//如何mQueue为null 则抛出异常 RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue"); Log.w("Looper", e.getMessage(), e); return false; } return enqueueMessage(queue, msg, uptimeMillis); }
Handler#
enqueueMessage:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis){ //保存当前的handle对象引用到msg的target中,target是handle重要的成员变量上文已经提到过了 msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } //调用MessageQueue的enqueueMessage方法完成发送信息的过程 return queue.enqueueMessage(msg, uptimeMillis); }
MessageQueue#enqueueMessage:
boolean enqueueMessage(Message msg, long when) { if (msg.target == null) {//msg.target为null即msg.target中存储的handle对象引用为null抛异常 throw new IllegalArgumentException("Message must have a target."); } ...... synchronized (this) { if (mQuitting) { //如何该线程looper已经结束,那么将抛出异常,例如在这之前你调用了looper的quit/quitSafely方法 IllegalStateException e = new IllegalStateException( msg.target + " sending message to a Handler on a dead thread"); Log.w(TAG, e.getMessage(), e); msg.recycle(); return false; } msg.markInUse(); msg.when = when; Message p = mMessages;//把当地保存的Message赋值给p boolean needWake; if (p == null || when == 0 || when < p.when) { // p == null证明在这之前还没有谁发送过Message //让msg.next指向p,其实就是指向了null(可以看作是指向队尾), //这个过程直接把msg插入队列中了,这时队列中还没有Message msg.next = p; mMessages = msg;//保存Message到本地变量mMessages中,即保存队头Message needWake = mBlocked; } else { // p != null这时队列中已经有Message needWake = mBlocked && p.target == null && msg.isAsynchronous(); Message prev;// 临时变量,保存当前p的指向 for (;;) { //这时队列中已经有Message,要遍历这个消息队列到队尾,然后把msg插入到队尾 //为什么要进行这一步?因为一个线程中可能有多个handle对象引用, //如果多个handle对象引用同时调用sendMessage方法发送信息的话,如果不遍历 //直接往消息队列中,那么msg插入的位置可能就不是队尾了,也就是说msg.next指向的不一定是队尾 prev = p;//保存当前p的指向到prev中 p = p.next;//指针下移 if (p == null || when < p.when) { break;//p == null证明已经到队尾,停止遍历 } if (needWake && p.isAsynchronous()) { needWake = false; } } msg.next = p; // 修改msg.next的指向,即指向队尾 prev.next = msg;//把msg插入到消息队列,此时prev.next的指向就是msg要插入的位置 } // We can assume mPtr != 0 because mQuitting is false. if (needWake) { //调用native函数完成把msg放置到消息队列中的过程 nativeWake(mPtr); } } return true; }
说明,把消息放置到消息队列中时,按照队列的规则是要把
消息插入到队尾,这种情况下就
不得不考虑多个handle的情况,需要对消息队列进行遍历,准确来说是从p当前的指向即mMessages(这里把p看作是指针更好理解些)遍历到队尾,最终把msg插入到队尾。过程如下图:
由handle创建的Message通过Looper把Message插入到消息队列中,此时消息队列已经有了五Message。
三 处理消息的流程
到这里应该已经知道
Message是在handle的handleMessage的方法中进行处理的,那么它又是如何与MQ(MessageQueue)关联一起的呢?如果够细心,你会发现在Looper的loop方法调用了dispatchMessage方法,如下:
Looper#loop:
/循环处理消息队列 public static void loop() { final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } final MessageQueue queue = me.mQueue; // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); for (;;) {//遍历消息队列 Message msg = queue.next(); // 调用next获取下一个msg 下面会详细介绍这个方法 if (msg == null) {//msg == null 遍历结束 // No message indicates that the message queue is quitting. return; } ....... try {//target就是handle类型,调用handle的dispatchMessage对消息进行处理 msg.target.dispatchMessage(msg); } finally { ....... } ......//回收资源 msg.recycleUnchecked(); } }Handle#dispatchMessage:
public void dispatchMessage(Message msg) { if (msg.callback != null) { //第一步:callback为Message的重要成员变量上文有介绍,其类型为Runnable,如果其不为null, //则调用handleCallback方法处理msg,其实内部就是调用Runnable的run方法。 handleCallback(msg); } else {//第二步:如果callback为null,mCallback不为null,mCallback为Handle的重要成员变量, //其类型为Callback,上文有介绍,这样情况下调用Callback的handleMessage方法。 if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } //第三步:如果callback为null,mCallback也为null,那么将会调用Handle中handleMessage方法进行处理Message handleMessage(msg); } }
说明,Message的处理过程可分为三步,如上,其实在介绍Handle的时候,提到的Message处理方式就是这里的第三种情况,说明我们使用的是最低级的Message处理方式。
MessageQueue#next分析:
Message next() { final long ptr = mPtr; if (ptr == 0) {//如果ptr == 0 证明已经退出looper,返回null return null; } ...... for (;;) {//循环遍历消息队列 ...... synchronized (this) { // Try to retrieve the next message. Return if found. final long now = SystemClock.uptimeMillis(); Message prevMsg = null; Message msg = mMessages;//队头Message赋值给msg if (msg != null && msg.target == null) {//如果设置了handle不会执行这块代码 ...... } if (msg != null) { if (now < msg.when) { ...... } else { //这一块才是关键性的代码,在上面已经介绍过,往消息队列中存放消息的时候p的指针从队头往队尾走, mBlocked = false; if (prevMsg != null) { prevMsg.next = msg.next; } else { mMessages = msg.next;//msg.next指向的Message保存到变量mMessages,即修改队头 } msg.next = null;//队头的Message已经取过,使它指向null,即销毁队头, //此时队头变为msg,即msg.next的 后一位 if (DEBUG) Log.v(TAG, "Returning message: " + msg); msg.markInUse(); return msg; } } else { ...... } ...... } ...... } }
Message取的过程如下图:
四 总结
Handle机制的大概流程如下:
(1) 在Looper.loop()方法运行开始后,循环地按照接收顺序取出MessageQueue里面的非NULL的Message。
(2)一开始Message Queue里面的Message都是NULL的。当Handler.sendMessage(Message)到MessageQueue,该函数里面设置了那个Message对象的target属性是当前的Handler对象。随后Looper取出了那个Message,则调用 该Message的target指向的Hander的dispatchMessage函数对Message进行处理。在dispatchMessage方法里,如何处理Message则由用户指定,三个判断,优先级从高到低:
a:Message里面的Callback,一个实现了Runnable接口的对象,其中run函数做处理工作;
b:Handler里面的mCallback指向的一个实现了Callback接口的对象,由其handleMessage进行处理;
c: 处理消息Handler对象对应的类继承并实现了其中handleMessage函数,通过这个实现的handleMessage函数处理消息。
由此可见,我们常使用的handleMessage方法是优先级最低的!
( 3)Handler处理完该Message (update UI) 后,Looper则设置该Message为NULL,以便回收!
在网上有很多文章讲述主线程和其他子线程如何交互,传送信息,最终谁来执行处理信息之类的,个人理解是最简单的方法——判断Handler对象里面的Looper对象是属于哪条线程的,则由该线程来执行!
(2)一开始Message Queue里面的Message都是NULL的。当Handler.sendMessage(Message)到MessageQueue,该函数里面设置了那个Message对象的target属性是当前的Handler对象。随后Looper取出了那个Message,则调用 该Message的target指向的Hander的dispatchMessage函数对Message进行处理。在dispatchMessage方法里,如何处理Message则由用户指定,三个判断,优先级从高到低:
a:Message里面的Callback,一个实现了Runnable接口的对象,其中run函数做处理工作;
b:Handler里面的mCallback指向的一个实现了Callback接口的对象,由其handleMessage进行处理;
c: 处理消息Handler对象对应的类继承并实现了其中handleMessage函数,通过这个实现的handleMessage函数处理消息。
由此可见,我们常使用的handleMessage方法是优先级最低的!
( 3)Handler处理完该Message (update UI) 后,Looper则设置该Message为NULL,以便回收!
在网上有很多文章讲述主线程和其他子线程如何交互,传送信息,最终谁来执行处理信息之类的,个人理解是最简单的方法——判断Handler对象里面的Looper对象是属于哪条线程的,则由该线程来执行!
好,本篇到此为止。
参考文献:
1《android的消息机制》
2《android进程与线程》