准备
在理解Handler之前,需要对于链表和数组的有一定的理解:
链表和数组都是用于存储数据的集合,所以他们的用图是相同的。那么他们两者有什么区别呢?换一种话来讲,就是什么时候使用数据,什么时候使用链表的问题了?
数组:有索引做下标,访问元素快,但是在插入操作的时候就较慢。在插入元素的时候指定插入的位置,可能需要移动其他元素的位置,才有位置供新元素进行插入。如下图所示,当需要在元素"B"前插入一个元素"E"时,我们需要找到元素"B"的 index 并将 index 上包括 1 之后的元素向后移动才能腾出位置给"E"。

链表:与数组相反,对于插入操作的时候较快,但是在访问元素时较慢。如下图所示,当需要在元素"B"前插入一个元素"D"时,我们需要找到元素"B"和记录下它前一个元素"A",然后将A.next指向元素"D",最后将D.next指向元素"B",即可完成,无需数组一样的元素移动。

正文
安卓开发中不能在子线程中更新UI的操作(不绝对,详情View绘制流程-未完成)。所以我们在子线程中需要使用Handler来发送Message来让主线程更新UI。

在上图中的代码主要是实现,在3S后将界面中的text文本更新为"handleMessage"。那么这主要是如何实现的呢?为什么会从子线程切换到主线程的呢?让我们看看 sendMessage() 发生了什么。
mHandler.sendMessage(msg);
我们点进方法中可以看到 sendMessage()的流程如下(参数忽略):
sendMessage() --> sendMessageDelayed() --> sendMessageAtTime()--> enqueueMessage()
Handler.java部分代码
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);
}
- 在sendMessage的时候,实际就是调用sendMessageDelayed只是delayMillis = 0 ;
- 在sendMessageAtTime时, SystemClock.uptimeMillis() + delayMillis 为当前系统时间 + 延时(注意);
- 在sendMessageAtTime中出现了MessageQueue进行存储Message;
- msg.target是Handler对象
既然出现了MessageQueue那么我们看看MessageQueue.enqueueMessage()里面发生了什么:
MessageQueue.java部分代码(方法中删除了干扰理解的代码)
Message mMessages;
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) {
...
msg.when = when;
Message p = mMessages;
if (p == null || when == 0 || when < p.when) {
//添加第一个message,或message的执行时间为0或或者小于表头的时间(可以看出mMessage为表头)
msg.next = p;
mMessages = msg;
} else {
...
//死循环,找出message执行时间对应的位置再跳出
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
...
}
//插入元素
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
...
}
return true;
}
- msg.when为之前传入的 SystemClock.uptimeMillis() + delayMillis
- mMessages为表头
- msg.next为Message对象
从上面可以看出,这就是文章开头的链表插入的操作。由于这里频繁的执行插入操作,并且插入的位置除了开头和结尾,还有中间。如果使用数组来存储的话,将会损耗较多的性能。
下面用图进行说明(在子线程中要发送三条Message,观察它们的存储情况)

- 在插入message1时,表头为null,所以 mMessages = message1;

2.在插入message2时, p!= null、when != 0 、 when > p.when, 进入else。开始死循环,当prev = messsage1,p = null,跳出死循环
message2.next = null; message1.next = message2;

3.在插入message3时,p!= null、when != 0、when > p.when,进入else。开始死循环,当prev = message1, p = message2时,message3.when < message2.when,跳出死循环
message3.next = message2; message1.next = message3;

以上分析了Message存储消息队列的流程。那么消息是怎么被取出来在主线程执行的呢?什么时候执行
public void handleMessage(Message msg);的呢?
那么就要引入另一个对象了Looper。不知道有多少人试过在,子线程中new Handler()对象。试过的人都知道会出现如下的错误:
错误代码
new Thread(new Runnable() {
@Override
public void run() {
Handler handler = new Handler();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Message message1 = Message.obtain();
message1.obj = "handleMessage";
mHandler.sendMessage(message1);
}
}).start();
报错信息

正确代码
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (null != msg.obj)
Log.i("tag", msg.obj.toString());
}
};
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Message message1 = Message.obtain();
message1.obj = "handleMessage1";
mHandler.sendMessage(message1);
Message message2 = Message.obtain();
message2.obj = "handleMessage2";
handler.sendMessage(message2);
Looper.loop();
}
}).start();
为什么要这样子写呢?我们看看Handler的构造方法:
Handler.java部分代码
public Handler() {
this(null, false);
}
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;
}
相信大家一定注意到了下面的代码,抛出的异常与控制台里的异常是一样的。且注意 mQueue = mLooper.mQueue;
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
那Looper.mLooper()返回的值为什么为空呢?让我们再进一步深入
Looper.java部分源码
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));
}
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
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;
...
//死循环
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
...
try {
msg.target.dispatchMessage(msg);
...
} finally {
...
}
...
}
}
- msg.target对应的是Handler对象
- ThreadLocal最后会解释
从mLooper() 方法中看出Looper对象是从ThreadLocal中得到的。它是通过调用prepare()方法实例化且保存的。认真的应该可以看出不仅在子线程调用了 Looper.prepare(); ,而且在子线程结尾处调用了 Looper.loop(); 如果这里没有该方法是无法让新建的Handler收到消息的,而且为什么它要放在线程的最后呢?
让我们看看在loop() 里执行了什么。拿到当前线程的 Looper 继而拿到 Looper 中的 MessageQueue。然后开始进入死循环, 当MessageQueue取出的Message不为空时,就调用 Handler的dispatchMessage()
。如果不放在最后,就会一直在死循环中,无法执行loop()之后的代码。
所以回到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()。不过是Message.CallBack为空的时候才调用类本身的handleMessage();
到目前为止是不是还有个疑问? 为什么在主线程中的new Handler()的时候不用Looper.prepare(),looper.loop();
ActivityThread.java部分源码
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
SamplingProfilerIntegration.start();
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("<pre-initialized>");
// --------------------------------------
Looper.prepareMainLooper();
// --------------------------------------
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
// --------------------------------------
Looper.loop();
// --------------------------------------
throw new RuntimeException("Main thread loop unexpectedly exited");
}
prepareMainLooper()方法
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
想必看到这两个方法就懂了吧!
所以为什么能在子线程中调用Handler来更新UI呢?
- 在子线程中添加Message到消息队列。
- 通过主线程的Looper来进行回调Handler中的dispatchMessage()方法。
- dispatchMessage()中调用handleMessage()方法。

插曲
ThreadLocal:提供线程内的局部变量,这种局部变量仅仅在线程的生命周期。每个线程中的变量不影响到其他线程。结合目前的情况就是 1 个线程对应 1个 Looper 对应 1 个MessageQueue
ThreadLocal.java部分代码
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
网友评论