这篇文章将会从以下几点对Handler进行分析
1. 如何使用Handler
2. Handler、Looper、MessageQueue如何建立关系
3. Handler发送消息后进行了哪些操作
4. Handler如何获取消息
如何使用Handler
在主线程中创建Handler实例
private Handler mAnimatorHandler;
private void initHandler() {
mAnimatorHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
......
}
};
}
根据代码需求在适当的时机 通过Handler发送消息到消息队列中
mAnimatorHandler.sendEmptyMessage(0);
这里Handler的使用不是本章的重点
如果不是很清楚Handler的使用,这里有很好的blog
Handler、Looper、MessageQueue如何建立关系
进入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();//创建looper对象
if (mLooper == null) {//判断looper
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;//创建一个MessageQueue,通过looper来赋值,handler和looper共用一个消息队列
mCallback = callback;
mAsynchronous = async;
}
从上面的操作和代码,我们可以看到在Handler构造方法中,又使用Looper.myLooper()方法创建了Looper对象,并判断Looper是否为空,若为空则抛出异常,异常内容是“没有调用 Looper.prepare()方法”,所以可以判断Looper.prepare() 在Looper中是个很重要的方法,若Looper不为空,则创建一个MessageQueue,通过Looper中的MessageQueue来赋值,自此Handler和Looper共用一个消息队列。
接下来我们去看Looper.prepare()方法
调用Looper中prepare() 方法创建Looper对象,才能保证之后Handler发送的消息添加到队列中
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {//不为空说明Looper被创建好了,不用再创建了
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));//将没有创建好线程的looper对象放入ThreadLocal容器中,以便下次能够使用
}
从源码中我们可以看到在Looper构造方法中创建MessageQueue对象
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);//创建MessageQueue对象
mThread = Thread.currentThread();
}
public static @Nullable Looper myLooper() {
return sThreadLocal.get();//获取looper对象
}
sThreadLocal是一个线程池
下面是找到这些源码的步骤:
查看源码的步骤
至此Handler、Looper、MessageQueue已经建立的关系,在创建Handler的同时创建了Looper对象,在Looper对象内部又创建了MessageQueue对象,并且Handler和Looper共用一个MessageQueue。
Handler发送消息后进行了哪些操作
当Handler调用sendEmptyMessage方法后又发生了什么,为什么主线程可以接收到发送的消息,下面我们带着这个问题来查看发送消息后的源码
发送消息后的源码查看通过以上操作我们发现在Handler中有很多发送消息的方法:
- sendMessage(Message msg)
- sendEmptyMessage(int what)
- sendEmptyMessageDelayed(int what, long delayMillis)
- sendEmptyMessageAtTime(int what, long uptimeMillis)
- sendMessageDelayed(Message msg, long delayMillis)
- sendMessageAtTime(Message msg, long uptimeMillis)
- sendMessageAtFrontOfQueue(Message msg)
不管是哪种发送消息的方法,都会走到MessageQueue类中enqueueMessage() 方法,这个方法的作用是向消息队列中插入消息,下面是enqueueMessage() 方法中插入消息的代码:
......
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;
......
另外MessageQueue类中还有一个重要的方法是next方法,作用是读取MessageQueue中的消息并删除这条消息,此方法在后面的轮询消息队列中会使用到。
Handler如何获取消息
发送消息后,将消息写入MessageQueue后,要怎么获取消息呢?
通过前面的分析我们知道Looper管理MessageQueue,因此在Looper的源码中所开放的方法中我找到loop() 这个方法是最重要的,此方法使用死循环的方式轮询消息队列,获取消息,虽然MessageQueue叫做消息队列,但实际上它的数据结构是一个单链表结构,下面是loop() 方法的重要部分的代码:
public static void loop() {
final Looper me = myLooper();//获取Looper对象
if (me == null) {//为空说明 Looper.prepare()方法没有被调用
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);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
}
既然loop() 方法是个死循环那么怎么跳出循环呢?Looper中有两个退出的方法,一个是quit(),另一个是quitSafely(),这两个方法的区别是quit会直接退出Looper,而quitSafely是设定了一个标记,当消息队列中的已有消息处理完毕后才安全的退出。所以想跳出循环,就得调用退出的方法。
查看轮询队列的相关源码从源码可以看出msg.target是一个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);
}
}
从源码中看msg.callback是一个Runnable
private static void handleCallback(Message message) {
message.callback.run();//在子线程做一些操作,执行run方法
}
当我们进入Callback接口中,我们终于见到了熟悉的handleMessage() 方法
public interface Callback {
/**
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
public boolean handleMessage(Message msg);
}
到这里我们就从Handler的创建到消息的发送再到接收消息的整个流程走完了
handler结构图.png最后,我还有一个问题那就是prepare() 方法是在哪里被调用的呢?带着这个问题,我们再去看看Looper类中的源码
/** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
* {@link #quit()}.
*/
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));
}
/**
* Initialize the current thread as a looper, marking it as an
* application's main looper. The main looper for your application
* is created by the Android environment, so you should never need
* to call this function yourself. See also: {@link #prepare()}
*/
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
哦,原来是在prepareMainLooper() 方法中调用的,prepareMainLooper() 方法主要是给主线程也就是ActivityThread创建Looper使用,ActivityThread被称为UI线程,也就是主线程。Looper中还提供了一个getMainLooper() 方法,通过它我们可以在任何地方获取到主线程的Looper。
网友评论