Handler信息机制详情
在线程内部有一个或者多个Hadnler对象,外部程序通过该Handler对象向线程发送异步消息,消息经由Hadnler传递到MessageQueue对象中,线程内部只能包含一个MessageQueue对象,主线程执行函数中从MessageQueue中读取消息,并回调Handler对象中的函数handleMessage()。
为更好地理解Handler的工作原理,先介绍有Handler一起工作的几个逐渐:
Message:Handler接收和处理的消息对象。
Looper:每个线程只能拥有一个Looper,它的loop方法负责读取MessageQueue中的消息,读到消息之后就把消息交给该消息对应的Hadnler进行处理。
MessageQueue:消息队列,它采用先进的方式来管理Message,程序创建Looper对象时会在它的构造器中创建Looper对象。
下面是线程内部Handler、MessageQueue、Looper类的调用过程。
我们通过调用Looper类的静态方法prepare()为线程创建MessageQueue对象,prepare()函数的代码如下:
- 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 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));
}
在该段代码中,变量sThreadLocal的类型是ThreadLocal,该类的作用是提供“线程局部存储”(在本线程内的任何对象保持一致),sThreadLocal对象会根据调用该prepare()函数的线程的id保存一个数据对象,这个数据对象就是所谓的“线程局部存储”对象,该对象是通过sThreadLocal的set()方法设置进去的,Looper类中保存的这个对象是一个Looper对象。prepare()函数的第一行先用get()方法去获取该线程对应的Looper对象,如果已经有的话,那么出错(因为一个线程只能有一个Looper对象,因为一个异步线程只能有一个消息队列),如果没有,创建一个新的Looper对象。
Looper的作用有两个:为该类静态函数的额prepare()的线程创建一个消息队列,第二个是提供静态的loop()函数,使调用该函数的线程进行无限的循环,并从消息队列中读取消息。
下面是Looper()对象的源码:
- private Looper(boolean quitAllowed) {
- mQueue = new MessageQueue(quitAllowed);//创建一个消息队列
- mRun = true;
- mThread = Thread.currentThread();
- }
- public static void loop() {
- final Looper me = myLooper();//返回当前的Looper对象,通过sThreadLocal的get方法
- 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();
- span style="white-space:pre"> </span>//进入无限循环
- 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);//完成对该消息的处理,也就是说,消息的具体处理
- //实际上是由程序指定的,msg变量的类型是Message,
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);//创建一个消息队列
mRun = true;
mThread = Thread.currentThread();
}
public static void loop() {
final Looper me = myLooper();//返回当前的Looper对象,通过sThreadLocal的get方法
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();
<span style="white-space:pre"> </span>//进入无限循环
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);//完成对该消息的处理,也就是说,消息的具体处理
//实际上是由程序指定的,msg变量的类型是Message,
- //msg.target的类型是Handler
- 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.recycle(); //每当处理完消息都需要调用,回收该Message对象占用
//msg.target的类型是Handler
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.recycle(); //每当处理完消息都需要调用,回收该Message对象占用
- //的系统资源,因为Message类内部使用了一个数据池保存Message //对象,从而避免了不停地创建和删除Message类对象,因此每次///处理完消息都需要将该Message对象表明为空,以便该对象可以被重用
- }
- }
//的系统资源,因为Message类内部使用了一个数据池保存Message //对象,从而避免了不停地创建和删除Message类对象,因此每次///处理完消息都需要将该Message对象表明为空,以便该对象可以被重用
}
}
下面说说MessageQueue。 该消息队列采用排队的方式对消息进行处理,即先到先处理,但如果消息本身被指定了被处理的时间,那么必须得等到该时间。队列中的消息以链表的结构进行保存。Message内部有一个next变量,指向下一个消息。
MessageQueue中主要有两个函数“取出消息”和“添加消息”。
分别为函数next()和enquenceMessage().
next()函数
先调用nativePollOnce(mPtr,int time)是一个JNI函数,他的作用是从消息队列中取出一个消息。MessageQueue本身并没保存消息队列,真正的消息队列数据保存在JNI中的C
代码中。也就是说会在C中创建一个NativeMessageQueue,这就是nativePollOnce第一个参数为int型变量的意义,在C中,该变量被强制转化为一个NativeMessageQueue对象,在C环境中,如果消息队列中没有消息,将导致当前线程被挂起,如果有,则C代码中将把该消息赋值给Java环境中的mMessages变量。
接下来的这段代码被包含在synchronize(this)关键字中,this被用作取消息和写消息的锁,这部分仅仅判断所指定的执行时间是否到了,如果到了,就返回该消息,并将mMessage变量置空。如果还没到,则尝试读取下一个信息。
如果mMessage为空,说明C环境中的消息队列没有可以执行的消息了,因此,执行mPendingIdleHandlers列表中的“空闲回调函数”,我们可以在MessageQueue中注册一些“空闲回调函数”,从而当线程中没有消息可以去执行这些“空闲代码”
- final Message next() {
- int pendingIdleHandlerCount = -1; // -1 only during first iteration
- int nextPollTimeoutMillis = 0;
- for (;;) {
- if (nextPollTimeoutMillis != 0) {
- Binder.flushPendingCommands();
- }
- nativePollOnce(mPtr, nextPollTimeoutMillis);
- synchronized (this) {
- if (mQuiting) {
- return null;
- }
- // 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 (false) Log.v("MessageQueue", "Returning message: " + msg);
- msg.markInUse();
- return msg;
- }
- } else {
- // No more messages.
- nextPollTimeoutMillis = -1;
- }
- // 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;
- }
- }
final Message next() {
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(mPtr, nextPollTimeoutMillis);
synchronized (this) {
if (mQuiting) {
return null;
}
// 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 (false) Log.v("MessageQueue", "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// 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;
}
}
下面是enquenceMessage(),
该函数分两步:
将参数msg赋值给mMessages.
调用nativeWake(mPtr),这也是一个JNI函数,其内部会将mMessage消息添加到C环境中的消息队列中,并且如果消息线程处在挂起状态,则唤醒该线程。
- final boolean enqueueMessage(Message msg, long when) {
- if (msg.isInUse()) {
- throw new AndroidRuntimeException(msg + " This message is already in use.");
- }
- if (msg.target == null) {
- throw new AndroidRuntimeException("Message must have a target.");
- }
- boolean needWake;
- synchronized (this) {
- if (mQuiting) {
- 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;
- if (p == null || when == 0 || when < p.when) {
- // New head, wake up the event queue if blocked.
- 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;
- }
- }
- if (needWake) {
- nativeWake(mPtr);
- }
- return true;
- }
final boolean enqueueMessage(Message msg, long when) {
if (msg.isInUse()) {
throw new AndroidRuntimeException(msg + " This message is already in use.");
}
if (msg.target == null) {
throw new AndroidRuntimeException("Message must have a target.");
}
boolean needWake;
synchronized (this) {
if (mQuiting) {
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;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
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;
}
}
if (needWake) {
nativeWake(mPtr);
}
return true;
}
下面是Handler的构造函数。
- public Handler(Callback callback, boolean async) {
- 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());
- }
- }
- mLooper = Looper.myLooper();
- if (mLooper == null) { //次数说明必须在构造Handler对象之前执行Looper.prepare()操作
- throw new RuntimeException(
- "Can't create handler inside thread that has not called Looper.prepare()");
- }
- mQueue = mLooper.mQueue;
- mCallback = callback;
- mAsynchronous = async;
- }
public Handler(Callback callback, boolean async) {
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());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) { //次数说明必须在构造Handler对象之前执行Looper.prepare()操作
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
调用驱动,操作系统提供的驱动一般都是C接口,Java本身不具备操作这些驱动的能力
对于某些大量数据的处理磨矿,Java的执行效率可能远低于C,因此希望用C去完成。
某些功能模块,可能两者执行效率差不多,但是已经存在C代码了,不想用Java再次去写,只想利用已有的C代码。
上一篇: 5.1源码解析Handler
下一篇: 中放绝对路径或者不在项目下图片的方法