最近复习了下Handler消息机制,跟了一下源码,准备做个小总结作为自身知识归纳,也可以跟博友们一起进行讨论讨论。
一、Handler是什么?它是一种消息机制,是进行线程间通信的。实际上Android的消息机制就是Handler的运行机制。
二、Handler的组成:handler、looper,masseage,messagequene
运行原理:handler通过发消息进入到messagequene中,message携带消息,looper轮询messagequene,将消息发出来,handler接受消息,处理逻辑。
上述运行原理是个非常浅显的,谁都知道的东西。但是深入底层,看底层代码运行原理的时候,我们先需要将每个成员都需要弄懂。
- handler:handler的作用就是通过post和send的一系列方法将消息发送到messagequene中。messagequenes收到消息之后,通过looper轮询,将消息处理。具体的细节通过看源码(以sendMessage为例):
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
这几段代码说明了通过handler.sendMessage走的handler底层的一系列方法,最终将消息放入到了messagequene中,然后looper通过轮询messagequene,然后将消息交给handler,handler处理消息。那这个过程我们看源码:
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;
}
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
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);
}
msg.recycleUnchecked();
}
}
looper.loop()这个方法就是一直来轮询messagequene的,for(;;)这是个死循环,也就是说handler通过send将消息发送到messagequene中,looper通过轮询messagequene,调用msg.target.dispatchMessage(msg);msg.target看Message源码发现,其实就是Handler对象,也就是调用的就是handler..dispatchMessage()方法。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
最后调用的就是handleMessage方法,我们在使用handler的时候会重写这个方法,然后处理逻辑。
小结Handler:Handler将消息发送到Messagequene中,Looper通过死循环Messagequene,获取消息,然后将消息交给Handler,Handler通过handleMessage方法处理掉消息。
2. Message:这个其实就是消息的载体,携带消息加入到Messagequene中。其中需要注意的是Message的对象三种创建方式
- 1.Message msg = new Message();
- 2.Message msg = Message.obtain();
- 3.Message msg = handler.obtainMessage();
这三种都是获取消息对象的方式,其中第二种和第三种使用起来较好(其实是一样的),因为obtain源码有个注释说是在Message消息池中返回一个Message实力,这样避免重复创建Message对象,容易造成内存泄漏。其中注意Message几个参数的用法:
- what:使用者定义这个消息用于区分这个消息到底是属于谁来处理的
- arg1:arg1和arg2相对于setData方法来讲,当你只是想携带int类型的数据时 我们的使用成本更加低廉
- arg2:arg1和arg2相对于setData方法来讲,当你只是想携带int类型的数据时 我们的使用成本更加低廉
- obj:obj可以传递任意类型的数据,但在进程间传递序列化的框架类时必须保证非空,并建议传递其他数据时还是以setData()为主; 设计obj的主要目的是传递引用数据类型,bundle是可以一次性传递多种基本数据类型的载体
3.Messagequene:消息队列,功能是插入和读取,这两个动作对应的方法也就是Messagequene的两个核心方法
enqueueMessage和next方法,其实Messagequene是通过一个单项列表数据结构进行数据维护的,是一个先进先出的规则,其中enqueueMessage方法就是单向链表的一个插入操作,具体看看next方法:
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {//死循环
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//阻塞方法,主要是通过native层的epoll监听文件描述符的写入事件来实现的。
//如果nextPollTimeoutMillis=-1,一直阻塞不会超时。
//如果nextPollTimeoutMillis=0,不会阻塞,立即返回。
//如果nextPollTimeoutMillis>0,最长阻塞nextPollTimeoutMillis毫秒(超时),如果期间有程序唤醒会立即返回。
nativePollOnce(ptr, 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 {
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 (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} 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(TAG, "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方法通过死循环不断轮询消息,如果有消息就执行,然后删除,没有消息就阻塞。
enqueueMessage这个方法处理了延迟发消息,其实就是messagequene是根据时间长短进行排序的,头时间最小,尾部时间最大,具体原理可查看其他博客有介绍,本章就不介绍了。
4.Looper:在handler里他扮演的一个不断从messagequene中轮询消息的角色,我们都知道Looper.prepare方法返回个looper对象,然后开启Looper.loop开始进行消息循环。首先我们要注意几点:
- 子线程启用Handler的时候一定要有Looper对象,否则会报错,在Looper的源码里有个loop()的方法,如果Looper对象为空,那么就会抛出个异常,这块可以看看源码我就不贴了,非常简单。
- 线程和线程进行交互的时候,主要获取当前的Looper就行,主线程默认有个Looper,同时Looper源码中也有获取主线程Looper的方法,具体可以看一下。
- Looper和线程是绑定的,他俩是一一对应的关系,你想往哪个线程发消息,就持有那个线程的Looper就可以。
- ThreadLocal这个是存储线程数据的类,它可以在指定的线程中存储数据。,其实存储的的就是当前线程的Looper对象
Looper的这个构造方法就是将当前的线程和Looper还有消息队列绑定起来。
在prepare的时候判断threadlocal是否获取到当前Looper对象,如果获取到了,就抛出个异常说明一个线程只能创建一个Looper,如果没获取到就将当前Looper对象存到threadlocal当中。
public void set(T value) {
Thread currentThread = Thread.currentThread();//得到当前线程
Values values = values(currentThread);// 得到当前线程的localValues变量的值
if (values == null) {//首次执行时,会进入这里
values = initializeValues(currentThread);//新创建一个Values对象
}
values.put(this, value);
}
ThreadLocal的set方法存入的其实是个键值对,键就是当前ThreadLocal对象,值就是Looper对象。Values是ThreadLocal的一个静态内部类,localValues是Thread的成员变量,
/**
* Creates Values instance for this thread and variable type.
*/
Values initializeValues(Thread current) {
return current.localValues = new Values();
}
初始化的方法其实就是返回个Values对象,其中ThreadLocal的值就是存储到Values内部的一个数组 中,取出的时候:
public T get() {
// Optimized for the fast path.
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values != null) {
Object[] table = values.table;
int index = hash & values.mask;
if (this.reference == table[index]) {
return (T) table[index + 1];
}
} else {
values = initializeValues(currentThread);
}
return (T) values.getAfterMiss(this);
}
get方法,返回的其实就是当前线程的localValues。其实从set和get方法中也能看出来操作的都是当前的localValues的table【】,所以,当你访问不同的线程对象的时候,其实都是在同一线程内部进行的读写操作。多以多线程的存储和修改数据的时候,是互相没有影响的。
所以说现在Handler消息机制的一些基本内容就复习完了,第一次写博客,可以留言进行相互讨论。