上一个文章讲解了Handler的基本使用,同时也有一些问题没有解决,本篇带你从源码的角度理解。
首先让我们来看看Handler的构造方法:
这三个构造方法都用@hide修饰,这表明这三个都不是我们开发者可以调用的。
接下来我们来看其他几个:
/**
* Default constructor associates this handler with the {@link Looper} for the
* current thread.
*
* If this thread does not have a looper, this handler won't be able to receive messages
* so an exception is thrown.
*/
public Handler() {
this(null, false);
}
/**
* Constructor associates this handler with the {@link Looper} for the
* current thread and takes a callback interface in which you can handle
* messages.
*
* If this thread does not have a looper, this handler won't be able to receive messages
* so an exception is thrown.
*
* @param callback The callback interface in which to handle messages, or null.
*/
public Handler(Callback callback) {
this(callback, false);
}
/**
* Use the provided {@link Looper} instead of the default one.
*
* @param looper The looper, must not be null.
*/
public Handler(Looper looper) {
this(looper, null, false);
}
/**
* Use the provided {@link Looper} instead of the default one and take a callback
* interface in which to handle messages.
*
* @param looper The looper, must not be null.
* @param callback The callback interface in which to handle messages, or null.
*/
public Handler(Looper looper, Callback callback) {
this(looper, callback, false);
}
通过代码我们发现所有的构造方法最终都会调用到以下两个:
1.当构造Handler时候没有主动传递Looper
/**
* Use the {@link Looper} for the current thread with the specified callback interface
* and set whether the handler should be asynchronous.
*
* Handlers are synchronous by default unless this constructor is used to make
* one that is strictly asynchronous.
*
* Asynchronous messages represent interrupts or events that do not require global ordering
* with respect to synchronous messages. Asynchronous messages are not subject to
* the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
*
* @param callback The callback interface in which to handle messages, or null.
* @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
* each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
*
* @hide
*/
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) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
2.当构造函数中传递了Looper
/**
* Use the provided {@link Looper} instead of the default one and take a callback
* interface in which to handle messages. Also set whether the handler
* should be asynchronous.
*
* Handlers are synchronous by default unless this constructor is used to make
* one that is strictly asynchronous.
*
* Asynchronous messages represent interrupts or events that do not require global ordering
* with respect to synchronous messages. Asynchronous messages are not subject to
* the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
*
* @param looper The looper, must not be null.
* @param callback The callback interface in which to handle messages, or null.
* @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
* each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
*
* @hide
*/
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
从代码的注释中可以看出传递Looper与不传递Looper区别是如果没有传递则使用当前Handler在哪个线程中声明就使用哪个线程的Looper。
接下来再看Hanlder中几个重要属性
从运行接口看出如果我们在构建Handler时传递了Callback则发送消息后不会回调Handler的handleMessage方法。
如果我们的Callback的hanldeMessage方法返回的结果是false呢的结果是啥子呢? 不错您答对了如果返回false会调用handler的handleMessage方法
我们用代码来证码,代码最会说实话:
如果是空则判断handler的mCallback属性是否为空,如果不为空则执行mCallback的handleMessage方法且如果返回true则return否则调用Handler的hanleMesssage。
我们再来看另外两个属性,mLooper和mQueue,要想弄懂这两个属性我们先要弄明白Handler是如何发送和处理消息的。处理消息的流程在上面我们已经讲明白了,下面我们讲发送消息的流程。
上一篇我们讲过发送消息的两种post和sendMessage。我们先来看post
这下就很清楚了三,runable参数设置给了Message的callback所有,所有调用post方法不会调用Handler的handleMessage方法。
接下来我们再看sendMessage方法:
/**
* Enqueue a message into the message queue after all pending messages
* before the absolute time (in milliseconds) uptimeMillis.
* The time-base is {@link android.os.SystemClock#uptimeMillis}.
* Time spent in deep sleep will add an additional delay to execution.
* You will receive it in {@link #handleMessage}, in the thread attached
* to this handler.
*
* @param uptimeMillis The absolute time at which the message should be
* delivered, using the
* {@link android.os.SystemClock#uptimeMillis} time-base.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the message will be processed -- if
* the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*/
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);
}
我们来看这个方法中有一个uptimeMillis那这个时间时用来干嘛的呢,这个时间用来指定发送这个消息的时间,相当于一种延时效果。
现在我们再来看将消息加入消息队列的方法:
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();//设置当前Message是正在使用标志位
msg.when = when;//设置message的发送时间
Message p = mMessages;//获取当前的message
boolean needWake;
if (p == null || when == 0 || 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
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;
}
1.如果当前msg的target为Null就会抛出异常这是为什么呢?因为target就是处理这个消息的Handler如果都没有人处理这个消息那这个消息就是不正确的消息。
2.如果当前消息正在使用中代表正在队列中或者马上被处理,也会抛出异常
3.当前handler已经没有与线程绑定也会抛出异常
4.根据发送时间来放置Message
5.调用native方法来唤醒Linux的epoll(消息循环)
从源码中我们可以看出MessageQuene中并没有任何保存Message的集合或者数组,其实这个消息队列的实现是通过一个链表来的每一个Message相当于一个结点,它是一个单向链表根据发送时间来排序,所以在牵涉到Message的时候又牵涉到了数据结构和算法,这个会在以后讲。
现在消息已经发送给并且保存到MessageQuene了但是又是如何将消息取出来的呢?我们接着往下面看MessageQuene有下面这个方法:
}
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();
}
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 // Next message is not ready. Set a timeout to wake up when it is ready. nextPollTimeoutMillis &#61; (int) Math.min(msg.when - now, Integer.MAX_VALUE); } else { // Got a message. mBlocked &#61; false; if (prevMsg !&#61; null) { prevMsg.next &#61; msg.next; } else { mMessages &#61; msg.next; } msg.next &#61; null; if (DEBUG) Log.v(TAG, "Returning message: " &#43; msg); msg.markInUse(); return msg; } } else { // No more messages. nextPollTimeoutMillis &#61; -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 &#61;&#61; null || now pendingIdleHandlerCount &#61; mIdleHandlers.size(); } if (pendingIdleHandlerCount <&#61; 0) { // No idle handlers to run. Loop and wait some more. mBlocked &#61; true; continue; } if (mPendingIdleHandlers &#61;&#61; null) { mPendingIdleHandlers &#61; new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; } mPendingIdleHandlers &#61; mIdleHandlers.toArray(mPendingIdleHandlers); } // Run the idle handlers. // We only ever reach this code block during the first iteration. for (int i &#61; 0; i final IdleHandler idler &#61; mPendingIdleHandlers[i]; mPendingIdleHandlers[i] &#61; null; // release the reference to the handler boolean keep &#61; false; try { keep &#61; 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 &#61; 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 &#61; 0; } } 首先我们来看第一个if判断 它是MessageQuene中的一个属性&#xff0c;是提供给native方法使用的 如果mPtr为0则返回null。那么mPtr是什么&#xff1f;值为0又意味着什么&#xff1f;在MessageQueue构造方法中调用了native方法并返回了mPtrmPtr &#61; nativeInit();&#xff1b;在dispose()方法中将其值置0mPtr &#61; 0;并且调用了nativeDestroy()。而dispose()方法又在finalize()中被调用。另外每次mPtr的使用都调用了native的方法&#xff0c;其本身又是long类型&#xff0c;因此推断它对应的是C/C&#43;&#43;的指针。因此可以确定&#xff0c;mPtr为一个内存地址&#xff0c;当其为0说明消息队列被释放了。 我们再看其中的另外一个if判断 这里的意思也很明显&#xff0c;当这个消息队列退出的时候&#xff0c;返回空。而且在返回前调用了dispose()方法&#xff0c;显然这意味着该消息队列将被释放。 我们来看剩下的代码&#xff0c;但是这段代码太长&#xff0c;我们来进行筛除 第一个要减的就是pendingIdleHandlerCount&#xff0c;这个局部变量初始为-1&#xff0c;后面被赋值mIdleHandlers.size();。这里的mIdleHandlers初始为new ArrayList()&#xff0c;在addIdleHander()方法中增加元素&#xff0c;在removeIdleHander()方法中移除元素。而我们所用的Handeler并未实现IdleHandler接口&#xff0c;因此在next()方法中pendingIdleHandlerCount的值要么为0&#xff0c;要么为-1&#xff0c;因此可以看出与该变量相关的部分代码运行情况是确定的&#xff0c;好的&#xff0c;把不影响循环控制的代码减掉。 第二个要减的是Binder.flushPendingCommands()这个代码看源码说明&#xff1a; Flush any Binder commands pending in the current thread to the kernel driver. This can be useful to call before performing an operation that may block for a long time, to ensure that any pending object references have been released in order to prevent the process from holding on to objects longer than it needs to. 这段话啥意不懂也没关系&#xff0c;这里只需要知道&#xff1a;Binder.flushPendingCommands()方法被调用说明后面的代码可能会引起线程阻塞。然后把这段减掉。 第三个要减的是一个log语句if (DEBUG) Log.v(TAG, "Returning message: " &#43; msg); 筛减后的代码 Message next() { int nextPollTimeoutMillis &#61; 0; for (;;) { nativePollOnce(ptr, nextPollTimeoutMillis); synchronized (this) { // Try to retrieve the next message. Return if found. final long now &#61; SystemClock.uptimeMillis(); Message prevMsg &#61; null; Message msg &#61; mMessages; if (msg !&#61; null && msg.target &#61;&#61; null) { // Stalled by a barrier. Find the next asynchronous message in the queue. do { prevMsg &#61; msg; msg &#61; msg.next; } while (msg !&#61; null && !msg.isAsynchronous()); } if (msg !&#61; null) { if (now // Next message is not ready. Set a timeout to wake up when it is ready. nextPollTimeoutMillis &#61; (int) Math.min(msg.when - now, Integer.MAX_VALUE); } else { // Got a message. mBlocked &#61; false; if (prevMsg !&#61; null) { prevMsg.next &#61; msg.next; } else { mMessages &#61; msg.next; } msg.next &#61; null; msg.markInUse(); return msg; } } else { // No more messages. nextPollTimeoutMillis &#61; -1; } // Process the quit message now that all pending messages have been handled. if (mQuitting) { dispose(); return null; } if (pendingIdleHandlerCount <&#61; 0) {//上面分析过该变量要么为0要么为-1 mBlocked &#61; true; continue; } } nextPollTimeoutMillis &#61; 0; } } 先获取第一个同步的message。如果它的when不晚与当前时间&#xff0c;就返回这个message&#xff1b;否则计算当前时间到它的when还有多久并保存到nextPollTimeMills中&#xff0c;然后调用nativePollOnce()来延时唤醒(Linux的epoll&#xff0c;有兴趣的自行百度)&#xff0c;唤醒之后再照上面那样取message&#xff0c;如此循环。代码中对链表的指针操作占了一定篇幅&#xff0c;其他的逻辑很清楚。 那么问题又来了&#xff0c;是谁在调用这个方法呢&#xff0c;那当然是我们还有一个没有讲的Looper洛&#xff0c;我们来看Looper的源码&#xff0c;同样我们只保留主流程的代码&#xff1a; /** * Run the message queue in this thread. Be sure to call * {&#64;link #quit()} to end the loop. */ public static void loop() { final Looper me &#61; myLooper(); if (me &#61;&#61; null) { throw new RuntimeException("No Looper; Looper.prepare() wasn&#39;t called on this thread."); } final MessageQueue queue &#61; me.mQueue; for (;;) { Message msg &#61; queue.next(); // might block if (msg &#61;&#61; null) { // No message indicates that the message queue is quitting. return; } try { msg.target.dispatchMessage(msg); } finally { if (traceTag !&#61; 0) { Trace.traceEnd(traceTag); } } msg.recycleUnchecked(); } } 1.首先判断Looper如果为Null则会抛出异常&#xff0c;为null的情况是在异步线程声明Handler时没有调用Looper.prepare()&#xff0c;当然根本不会出现因为如果在异步线程中没有调用该方法运行时会直接报错 2.一个死循环从消息队列中循环消息&#xff0c;如果出现消息为null的情况说明已经是被回收了的 msg.target.dispatchMessage(msg)&#xff0c;这部很关键我们知道msg的targe是Handler所以这是在调用handler的dispatchMessage方法进行处理&#xff0c;这也是为什么Handler在哪个线程声明中就在哪个线程中处理消息和哪个handler发送消息哪个handler处理的由来&#xff0c; 上面就是消息从发送到处理的流程&#xff0c;通过文字理解起来比较抽象&#xff0c;我们来画个图具体的描述下 实现了Parcelable接口 这是android种用来进行数据传输的&#xff0c;先记住后面的章节会具体的讲 我们平时声明Message对象有两种方法直接new或者Message.obtain()这个方法有以下的几种重写 我们接着看无参构造方法&#xff1a; /** * 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 !&#61; null) { Message m &#61; sPool; sPool &#61; m.next; m.next &#61; null; m.flags &#61; 0; // clear in-use flag sPoolSize--; return m; } } return new Message(); } 看到里面有一把锁&#xff0c;这个是线程同步&#xff0c;如果当前message不null则把当前的赋值给一个新的引用&#xff0c;sPoolSize是指message池的大小最大为50
var cpro_id = "u6885494";