Android的消息机制之Handler的运行机制,Handler是Android消息机制的上层接口,消息机制由(Handler、Looper、Message、MessageQueue)构成:
Android的消息机制主要是指Handler的运行机制,Handler
的运行需要底层的MessageQueue和Looper的支撑。
MessageQueue
的翻译为消息队列,它的内部存储了一组消息(Message
),以队列的形式对外提供插入和删除的工作。但是它的内部存储结构并不是真正队列,而是采用单链表的数据结构来存储消息列表。
Looper
翻译为消息循环。由于MessageQueue只是一个消息的存储单元,它不能去处理消息,而Looper就填补了这个功能,Looper会以无限循环的形式去查找是否有新消息,如果有就去处理消息,否则一直等待。
线程默认没有Looper的,如果需要使用Handler就必须为线程创建Looper。我们经常提到的主线程,也叫UI线程,它就是ActivityThread,ActivityThread被创建时就会初始化Looper,这也是在主线程中默认可以使用Handler的原因。
Handler创建的时候会采用
当前线程
的Looper
来构造消息循环系统,Looper在哪个线程创建就跟哪个线程绑定
,并且Handler是在它关联
的Looper对应的线程中处理消息的。
Handler的工作主要包括消息的发送和接收过程。消息的发送可以通过post的一系列的方法和send的一系列的方法来实现。post的一系列方法最终是通过send的一系列的方法来实现的。下面有三个实例来实现handler实例。实例1和实例2是通过send系列方法发送和接收的,实例3是通过post方法发送和接收的。实例1我们经常使用这种方法,实例2是通过Handler handler = new Handler(callback)
来构造handler,源码里说明为:可以用来创建一个handler的实例但并不需要派生handler的子类。在日常开发中,创建handler的的最常用的方式是派生一个handler的子类并重写其handleMessage方法来处理具体的消息,而callback给我们提供另一种使用handler的方式,当我们不想派生子类时,可以使用callback来实现。
//实例1
Handler handler1 = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.i("what:",""+what);
}
};
handler1.sendEmptyMessage(1001);
//实例2
Handler handler2 = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message message) {
int what = message.what;
Log.i("what:",""+what);
return true;
}
});
handler2.sendEmptyMessage(1002);
//实例3
new Thread(new Runnable() {
@Override
public void run() {
Log.i("wang","step 1:"+Thread.currentThread().getName());
//通过获取主线程的Looper来构造handler
Handler handler = new Handler(getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
//可以在此更改UI的操作
Log.i("wang","step 2:"+Thread.currentThread().getName());
}
});
}
}).start();
//用当前线程和一个回调接口和是否异步来初始化一个Handler
public Handler(Callback callback, boolean async) {
//匿名类、内部类或本地类都必须申明为static,否则会警告可能出现内存泄露
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
//获取当前线程的Looper,必须先执行Looper.prepare(),才能获取Looper对象,否则为null.
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;//消息队列,来自Looper对象
mCallback = callback; //回调方法
mAsynchronous = async;//设置消息是否为异步处理方式
}
// 设置一个默认值来初始化handler
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
在Looper.loop()中会有一段msg.target.dispatchMessage(msg);
,msg.target
为当前使用的handler。当发现有消息时,调用消息的目标handler,执行dispatchMessage()方法来分发消息。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
//当Message存在回调方法,回调msg.callback.run()方法;
handleCallback(msg);
} else {
if (mCallback != null) {
//当Handler存在Callback成员变量时,回调方法handleMessage();
if (mCallback.handleMessage(msg)) {
return;
}
}
//Handler自身的回调方法handleMessage(),一般使用【2.1】中的实例1时,调用的是这个方法
handleMessage(msg);
}
}
所有的发送消息的方式如图所示,最终都是要调用MessageQueue.enqueueMessage()
;
//最终发送消息调用的方法
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//msg.target为Handler对象,在这里将Message和当前Handler关联起来了
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
//Handler.obtainMessage()方法,最终调用Message.obtainMessage(this),其中this为当前的Handler对象。
public final Message obtainMessage() {
return Message.obtain(this); //调用【3.2 obtain()】
}
//Handler删除Message
public final void removeMessages(int what) {
mQueue.removeMessages(this, what, null);//调用【4.5 removeMessages】
}
Handler是消息机制中非常重要的辅助类,更多的实现都是MessageQueue, Message中的方法,Handler的目的是为了更加方便的使用消息机制。
数据类型 | 成员变量 | 解释 |
---|---|---|
int | what | 消息类别 |
long | when | 消息触发时间 |
Object | obj | 发给接收者的对象 |
Handler | target | 消息的当前handler的对象 |
Runnable | callback | 回调方法 |
int | arg1 | 参数1 |
int | arg2 | 参数2 |
//静态变量sPool的数据类型为Message,通过next成员变量,维护一个消息池
private static Message sPool;
//静态变量MAX_POOL_SIZE代表消息池的可用大小
private static int sPoolSize = 0;
//消息池的默认大小为50
private static final int MAX_POOL_SIZE = 50;
/**
* 从消息池中得到Message
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*/
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;//从sPool中取出一个Message对象,并消息链表断开
sPoolSize--;//消息池的可用大小进行减1操作
return m;
}
}
return new Message(); // 当消息池为空时,直接创建Message对象
}
/**
* Return a Message instance to the global pool. You MUST NOT touch
* the Message after calling this function -- it has effectively been
* freed.
* 把不再使用的消息加入消息池
*/
public void recycle() {
clearForRecycle();
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
//对于不再使用的消息,加入到消息池
void clearForRecycle() {
flags = 0;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
when = 0;
target = null;
callback = null;
data = null;
}
消息队列在android中指的是MessageQueue,MessageQueu主要包含两个操作:插入和读取。读取操作本身会伴随着删除的操作,插入和读取对应的方法分别为enqueueMessage和next,其中enqueueMessage的作用是往消息队列中插入一条消息,而next的作用是从消息队列读取一条消息并将从消息队列中移除。消息队列的数据结构为一个单链表来维护消息队列。
// True if the message queue can be quit.
private final boolean mQuitAllowed;
private int mPtr; // used by native code
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
//通过native方法初始化消息队列,其中mPtr是供native代码使用
mPtr = nativeInit();
}
boolean enqueueMessage(Message msg, long when) {
if (msg.isInUse()) {
throw new AndroidRuntimeException(msg + " This message is already in use.");
}
//每一个Message都必须有一个handler对应
if (msg.target == null) {
throw new AndroidRuntimeException("Message must have a target.");
}
synchronized (this) {
if (mQuitting) {
RuntimeException e = new RuntimeException(
msg.target + " sending message to a Handler on a dead thread");
Log.w("MessageQueue", e.getMessage(), e);
return false;
}
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
//p为null(代表MessageQueue没有消息) 或者msg的触发时间是队列中最早的, 则进入该该分支
msg.next = p;
mMessages = msg;
needWake = mBlocked;//当阻塞时需要唤醒
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
Message next() {
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// We can assume mPtr != 0 because the loop is obviously still running.
// The looper will not call this method after the loop quits.
nativePollOnce(mPtr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
//当消息Handler为空时,查询MessageQueue中的下一条异步消息msg,则退出循环。
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
//当异步消息触发时间大于当前时间,则设置下一次轮询的超时时长
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (false) Log.v("MessageQueue", "Returning message: " + msg);
msg.markInUse();
return msg;//成功地获取MessageQueue中的下一条即将要执行的消息
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf("MessageQueue", "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
next方法是一个无限循环的方法,如果队列中没有消息,那么next方法会一直阻塞在这里。当有消息到来时,next方法会返回这条消息并将其从单链表中移除。
nativePollOnce是阻塞操作,其中nextPollTimeoutMillis代表下一个消息到来前,还需要等待的时长;当nextPollTimeoutMillis = -1时,表示消息队列中无消息,会一直等待下去。
当处于空闲时,往往会执行IdleHandler中的方法。当nativePollOnce()返回后,next()从mMessages中提取一个消息。
nativePollOnce()在native做了大量的工作,想进一步了解可查看 大神写的Android消息机制2-Handler(native篇)。
void removeMessages(Handler h, int what, Object object) {
if (h == null) {
return;
}
synchronized (this) {
Message p = mMessages;
//从消息队列的头部开始,移除所有符合条件的消息
while (p != null && p.target == h && p.what == what
&& (object == null || p.obj == object)) {
Message n = p.next;
mMessages = n;
p.recycleUnchecked();
p = n;
}
//移除剩余的符合要求的消息
while (p != null) {
Message n = p.next;
if (n != null) {
if (n.target == h && n.what == what
&& (object == null || n.obj == object)) {
Message nn = n.next;
n.recycleUnchecked();
p.next = nn;
continue;
}
}
p = n;
}
}
}
Last but not least,Looper在android消息机制中扮演者消息循环的重要角色,具体来说就是它会不停地从MessageQueue中查看是否有新消息,如果有新消息就会立即处理,否则就一直阻塞在那里。
final MessageQueue mQueue;
final Thread mThread;
private Looper(boolean quitAllowed) {
//创建MessageQueue对象
mQueue = new MessageQueue(quitAllowed);
//记录当前线程.
mThread = Thread.currentThread();
}
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
可以看到,对于每个线程只能有一个Looper。也就是说执行prepare方法时,必然执行最后一行代码sThreadLocal.set(new Looper(quitAllowed));
,MessageQueue 就被创建了。这里也可以看到,默认情况下,一个MessageQueue的quiteAllow=true
这里使用到的sThreadLocal 是一个ThreadLocal对象。简单来说,使用它可以用来解决多线程程序的并发问题。使用set方法,将此线程局部变量的当前线程副本中的值设置为指定值;使用get方法,返回此线程局部变量的当前线程副本中的值。
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
final Looper me = myLooper();//获取ThreadLocal存储的Looper对象
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;//获取Looper对象中的消息队列
//确保在权限检查时基于本地进程,而不是基于最初调用进程。
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {//进入loop的主循环方法
Message msg = queue.next(); // might block
if (msg == null) {
//没有消息,则退出循环
return;
}
//默认为null,可通过setMessageLogging()方法来指定输出,用于debug功能
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
//用于分发Message
msg.target.dispatchMessage(msg);
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
//将Message放入消息池
msg.recycle();
}
}
loop方法是一个死循环,唯一跳出循环的方式是MessageQueue的next方法返回了null。当Looper的quit方法被调用时,Looper就会调用MessageQueue的quit或者quitSafely方法来通知消息队列退出,当消息队列被标记为退出状态时,它的next方法就会返回null。也就是说,Looper必须退出,否则loop方法就会无限循环下去。loop方法会调用MessageQueue的next方法 来获取新消息,而next是一个阻塞操作,当没有消息时,next会一直阻塞在那里,这也导致loop方法一直阻塞在那里。如果MesageQueue的next方法返回了新消息,Looper就会出来这条消息:msg.target.dispatchMessage(msg);
,这里的msg.target是发送这条消息的Handler对象,这样Handler发送的消息最终又交给它的dispatchMessage方法来处理了。但是这里不同的是,Handler的dispatchMessage方法是创建在Handler时所使用的Looper中执行的,这样就成功的将代码逻辑切换到指定的线程中去执行了。
Handler 的主要作用就是将一个任务切换到指定的线程中去执行,这个切换线程指的是任何线程,不单单切换到主线程。
那么最后思考,在整个机制中到底是那个地方的原理使得handler达到切换线程的目的呢?
简单来说,handler 是用于同一个进程中线程之间的通信。looper 让主线程无限循环的从自己的 messageQueue 拿消息处理,所以我们可以知道处理消息是在主线程中处理的,那么是怎样在其他的线程往主线程队列里放入消息的呢?道理其实很简单,我们知道同一进程中线程和线程之间资源是共享的,也就是对于任何变量在任何线程都是可以访问和修改的,只要考虑并发性和同步性接可以了。那么只要拿到 messageQueue 的实例,就可以往主线程的 messageQueue 放入消息,主线程在轮询的时候就可以在主线程处理这个消息。那么怎样拿到主线程的 messageQueue 的实例呢?这个就是 handler 类的作用了,只要在主线程中构建 handler 类,那么这个 handler 实例就获取到主线程 messageQueue 实例的引用(在构造方法中,mLooper = Looper.myLooper(); mQueue = mLooper.mQueue;),handler 在sendMessage 的时候就通过这个引用(msg.target = this;)往消息队列中插入数据。handler 的另一个作用,就是能统一处理消息的回调,这样handler 发出消息又确保消息处理也是自己做出来的,具体的做法就是队列中的message 持有 handler 的引用(哪个handler 把它放到队列中,message 就持有这个 handler 的引用),然后等到主线程轮询到这个 message 的时候,就来回调我们经常重写的 handler 的handleMessage(Message msg)方法。
站在巨人的肩膀上:
文章参考了 任玉刚-《Android开发艺术探索》
文章参考了Android消息机制1-Handler(Java层)
文章参考了android之handler切换线程终极篇