关于handler实现原理的几个问题 源码分析
1.消息队列的数据结构和实现方式;
2.如何保持线程状态,不被销毁;
3.Message如何优先执行以及确保线程安全;
4.如何直接在主线程中执行
5.关于队列,我常用的实现方式
以下将根据源码来具体谈谈这几个问题,源码使用SDK 28。
实现原理主要包含下面几个类:
MessageQueue:消息队列,在构造方法里初始化Native,持有队列第一个Message,循环遍历队列,队列为 空时阻塞线程。
Handler:发送和处理Message类,包含同步和异步、异步阻塞等结果,获取Message(通过 Message.obtain复用Message)
Message:消息的实体类,包含下一个Message,并有一个静态的Message复用池,还有一些初始化参数等。
Looper:为当前线程创建MessageQueue,并遍历消息队列。
先说一下流程:
1.Looper.prepare()为当前线程创建MessageQueue,Looper.loop()调用MessageQueue.next()方法遍历消息队列 (注:队列为空时next()方法会阻塞);
2.Handler.sendMessage()方法调用MessageQueue.enqueueMessage()方法按照时间顺序向消息队列里面添加Message,如果线程阻塞则唤醒。
3.Looper.loop()遍历消息队列,取出Message,根据Message的target(Handler类型,发送消息时绑定的)调用Handler的dispatchMessage()方法,dispatchMessage()方法会调用handleMessage()方法回调Handler所属的线程处理。(注:dispatchMessage()方法会优先调用Message的callback接口(Runnable 类型),其次是Handler的mCallback.handleMessage(),最后是Handler的handleMessage()方法)
注:源码重点部分会加中文注释
1.消息队列的数据结构和实现方式
Message的结构类似于链表,根据每个Message可以获得下一个Message,而MessageQueue持有Message队列的第一个Message.
// Message源码,包含一个Message类型的变量next
// sometimes we store linked lists of these things
/*package*/ Message next;
如何保证Message按照时间的顺序执行呢?下面是MessageQueue的enqueueMessage方法,重点代码在我加的中文注释里。
/**
* 每一个Message都有一个执行的时刻表,根据时刻表顺序加入到队列
*/
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
if (mQuitting) {
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;
boolean needWake;
// 队列里面的Message为空时表明线程处于需要唤醒的状态,
// 当时刻表等于0或者时刻表小于队列第一Message的时刻表时,加入到队列最前面
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;
// 把Message按照时间顺序插入到队列
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) {
// Native层唤醒阻塞的线程
nativeWake(mPtr);
}
}
return true;
}
2.如何保持线程状态,不被销毁
实现的关键就在MessageQueue的next()方法里面。在遍历消息队列的时候阻塞线程。在run()使用Looper.loop()方法之后,后面的run()里面的代码将不再执行
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层获取,如果没有则阻塞
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.
// 剩余Message数量为0,修改变量为阻塞,下一次取消息时将在nativePollOnce()方法阻塞
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;
}
}
3.Message如何优先执行
其实这个已经在第一个问题里面解释了,在加入到消息队列时,遍历消息队列按照时间顺序插入到指定位置。这里面涉及到线程安全问题,即发送消息的线程和Handler本身所属的线程同时操作MessageQueue,请看入队时的这段代码。
boolean enqueueMessage(Message msg, long when) {
/..../
//在入队时同步当前MessageQueue对象。
synchronized (this) {
if (mQuitting) {
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;
}
/..../
}
return true;
}
4.如何直接在主线程中执行?
这个问题我们只关注Handler实现的这几个类里面的,其他方式不作探讨。
Handler里面的getMain()方法已经被隐藏了(实际上也是根据Looper来实现的),通过Looper里面有一个静态Looper对象,该对象属于主线程的。然后根据Looper来创建Handler对象,通过Handle的post()方法可直接运行Runnable。这也表明:多个Handler可以共用一个Looper,即能共用消息队列。遍历时只能遍历一个消息队列,即一个Handler有且只能有一个Looper
(注:在初始化Handler的时候会获取当前线程的Looper,如果为空,则报异常。所以除主线程已经包含Looper之后,其他的线程在创建Handler之前都必须先创建Looper对象(调用Looper.prepare()))
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
// 在这里直接执行
}
});
5.关于队列,我常用的实现方式
我之前写队列操作喜欢用java的方式实现。比如用Semaphore的方式来阻塞线程。当队列为空时阻塞,当消息加入时释放Semaphore。见下面代码:对比Handler机指,
public class LightUtil extends Thread
{
private static final String TAG = LightUtil.class.toString();
private static LightUtil mInstance;
private volatile boolean mStart = false;
ConcurrentLinkedQueue<Message> messages;
Semaphore semaphore = new Semaphore(0);
private LightUtil() {
super("LightUtil");
messages = new ConcurrentLinkedQueue<Message>();
mStart = true;
start();
}
public static LightUtil getInstance(){
if (mInstance == null)
{
synchronized (LightUtil.class)
{
if (mInstance == null)
{
mInstance = new LightUtil();
}
}
}
return mInstance;
}
@Override
public void run() {
super.run();
while (mStart)
{
try {
semaphore.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
try {
Thread.sleep(100);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
continue;
}
Message message = messages.poll();
while (message != null)
{
exeMsg(message);
message = messages.poll();
}
}
}
public void addMeesage(Message message){
messages.add(message);
semaphore.release();
}
public static class Message{
}
}
Handler的优势:
1.有优先级控制(根据设置的执行时间)
2.可以执行多种类型的任务;
最后:消息队列以及线程池的主要作用就是线程复用。而线程复用的基本实现都是通过阻塞线程,减少线程的创建和销毁(线程创建和销毁开销很大)。
来源:CSDN
作者:wengliuhu
链接:https://blog.csdn.net/wengliuhu/article/details/104058445