手把手带你从源码的角度全面理解Handler、Looper、Me
前言:
以前都是看别人的博客、书来分析Handler的源码,没有自己真正系统的研究源码,导致虽然看了那么多博客,还是似懂非懂,理解的不够透彻,所以说看别人的博客只是帮助你理解,还是得自己去看源码深入研究下,才能真正理解透彻、全面。下面是我自己看源码后的一些心得体会,希望能对大家研究源码起到一定的辅助作用.
目标:
首先要明确几个目标,我们要解决以下几个问题:
a.线程是如何与Looper关联起来的?
b.一个线程里面可以有几个Looper?
c.消息是怎么从一个线程传递到另一个线程的?
d.Handler、Looper、MessageQueue三者之间的关系是怎样的?
分析源码前,先举个栗子:
MessageQueue相当于一个池塘,Message就是池塘里面的水,Looper就是一台抽水机,现在我们要用这台抽水机把池塘里的水抽出来,但是首先我们这个池塘开始是没有水的,Handler就相当于一个人,人首先会往池塘里放水(调用handler.sendMessage()等方法),然后还要记得的是,要给我们的抽水机Looper插上电啊,没电你怎么抽水?
插电就是调用Looper.loop()方法,水从池塘抽出来后,又会交给人Handler去处理(通过handler的handleMessage)。
下面我们就开始分析源码吧:
通常我们使用Handler来做Android线程间通信都会在new一个Handler对象,那么我们便从Handler的构造方法看起
1、
public Handler() {
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 = null;
}
我们可以看到上面的
mLooper = Looper.myLooper();方法
那么我们进入这个方法一探究竟
2、
public static final Looper myLooper() {
return (Looper)sThreadLocal.get();
}
它只是返回sThreadLocal的get()方法后强转成了Looper对象。
那么sThreadLocal又是个是啥呢?咱先不去管它。咱先去看看它的get()方法。代码如下:
3、
@SuppressWarnings("unchecked")
public T get() {
// Optimized for the fast path.
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values != null) {
Object[] table = values.table;
int index = hash & values.mask;
if (this.reference == table[index]) {
return (T) table[index + 1];
}
} else {
values = initializeValues(currentThread);
}
return (T) values.getAfterMiss(this);
}
可以看到它首先调用了Thread currentThread = Thread.currentThread();获取到了当前线程,这个大家都很熟悉,就是说我们在哪个线程里面new Handler();拿到的自然也就是哪个线程。然后下面一行代码又调用Values values = values(currentThread);方法,传入当前线程对象获得了一个Values对象,那么这个Values又是个啥呢?它是ThreadLocal的一个静态内部类,Values其实就是Map,它的实现方式基本和HashMap一样(这个大家可以自己去仔细阅读源码,我这里就不贴出来了,代码有些长),那么我们继续看这个values(Thread)方法.
Values values(Thread current) {
return current.localValues;
}
这个方法是ThreadLocal的一个方法,它的代码很简单,只有一行,返回了传入线程对象的localValues属性,而这个Thread的localValues属性在Thread类里面是这样声明的:
ThreadLocal.Values localValues;
可以看出它其实就是一个Values对象,其实就是一个Map集合,而且在Thread类里面并没有对它进行初始化。那么我们回到上面标号为3的代码处的Values values = values(currentThread)这句代码,现在我们便知道了原来这句代码是返回了当前线程currentThread的一个属性localValues,而这个属性就是一个Map集合。
然后继续往下看get方法里的这段代码
if (values != null) {
Object[] table = values.table;
int index = hash & values.mask;
if (this.reference == table[index]) {
return (T) table[index + 1];
}
} else {
values = initializeValues(currentThread);
}
之前我们也说了Thread类里面localValues并没有初始化,也就是我们拿到的values对象是为null的,那么理所当然它走的是else代码块,可以看到里面对values进行了赋值。很简单一行代码,给values赋值。
Values initializeValues(Thread current) {
return current.localValues = new Values();
}
最后get()方法的返回值(T) values.getAfterMiss(this);之前说了,values其实是一个HashMap,它的getAfterMiss其实就是和HashMap的get方法一样,只是换了个名字罢了。阅读源码其实可以发现Values它是以ThreadLocal对象为键,Looper对象为值的,所以ThreadLocal和Looper是一一对应的,而ThreadLocal又是和当前线程一一对应,所有Looper也和当前线程一一对应了。因为是ThreadLocal里面调用这个方法,所以在values.getAfterMiss(this)方法传的这个this就是ThreadLocal对象,取出与一一对应的Looper返回。
最后回到标号为 2的代码, return (Looper)sThreadLocal.get();这句就是返回了与sThreadLocal一一对应的Looper对象。
然后我们再看到标号为1的那段代码,在 mLooper = Looper.myLooper();调用后会有下面这个判断:
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
```
就是说当前mLooper为空就会报异常,必须先创建一个Looper对象才行。其实在主线程中已经默认为我们创建了这个Looper对象(这个可以在ActivityThread的main方法中看到)。但是在如果你想要在子线程中new一个Handler去处理消息前,就必须去调用Looper.prepare() 去创建一个Looper。代码如下:
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}
可以看到prepare方法里面首先就做了一个判断sThreadLocal.get()是否为空,这个方法我们已经分析过了,它返回的是当前线程(new Handler的那个线程)对应的Looper对象,如果不为空,就会抛异常,也就证明一个线程里面只能有一个Looper,创建两次或多次就会出错,如果为空,会看到它又调用了ThreadLocal的set方法,并把new出来的Looper作为参数传了进去。set方法具体代码如下:
public void set(T value) {
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values == null) {
values = initializeValues(currentThread);
}
values.put(this, value);
}
之前我们分析过ThreadLocal的get方法,那么它的set方法也是差不多的,最后那句代码以当前ThreadLocal对象为键,value(也就是我们的Looper对象)为值存入到values这个map中。
下面我们再来分析MessageQueue:
首先看一段在线程中new Hnadler的常见代码:
new Thread(new Runnable() {
public void run() {
Looper.prepare();
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
......
}
};
handler.sendEmptyMessage(1);
Looper.loop();
};
}).start();
Looper.prepare之前我们已经说到了,主要是下面的Looper.loop()方法,我们进入这个方法瞧瞧:
public static final void loop() {
Looper me = myLooper();
MessageQueue queue = me.mQueue;//第3行代码
while (true) {
Message msg = queue.next(); // might block
//if (!me.mRun) {
// break;
//}
if (msg != null) {
if (msg.target == null) {
// No target is a magic identifier for the quit message.
return;
}
if (me.mLogging!= null) me.mLogging.println(
">>>>> Dispatching to " + msg.target + " "
+ msg.callback + ": " + msg.what
);
msg.target.dispatchMessage(msg);//第18行代码
if (me.mLogging!= null) me.mLogging.println(
"<<<<< Finished to " + msg.target + " "
+ msg.callback);
msg.recycle();
}
}
}
第3行代码获得了1个当前Looper的MessageQueue对象,那么我们再去看看me.mQueue是在什么时候初始化的,代码如下:
private Looper() {
mQueue = new MessageQueue();
mRun = true;
mThread = Thread.currentThread();
}
从上面这段代码看出,它是在Looper的构造方法里面就给创建了。还记得不,Looper.prepare()方法最后那行代码sThreadLocal.set(new Looper()),也就是说在Looper.prepare()调用的时候我们的 MessageQueue就已经创建了。
然后我们继续看loop方法,里面用while(true)开启了一个死循环,不断的从MessageQueue取Message进行处理。
再看到loop方法的第18行代码 调用了msg.target.dispatchMessage(msg),那么msg.target的又是个什么鬼?从意思上知道它是"目标",进入到Message里面看可以知道它就是一个Handler。我们再调用handler.sendEmptyMessage(1); 发消息的时候其实最终会调用Handler的sendMessageAtTime(Message msg, long uptimeMillis)方法(可自行看源码,为了简便,中间省了一些过程),代码如下:
public boolean sendMessageAtTime(Message msg, long uptimeMillis){
boolean sent = false;
MessageQueue queue = mQueue;
if (queue != null) {
msg.target = this;//第5行代码
sent = queue.enqueueMessage(msg, uptimeMillis);//第6行代码
}
else {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
}
return sent;
}
看到这段代码的第5行, msg.target = this; msg.target 赋了值,值就是当前的Handler。再看到第6行代码,它将我们发送的消息加入了消息队列MessageQueue。那么一切就解释的通了。
handler发送消息就是将消息加入消息队列里面去,然后Loop.loop()方法将消息队列里面的消息不断的取出来进行处理,进行处理的方法就是:msg.target.dispatchMessage(msg)方法,代码如下:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
可以看到最后哪行代码handleMessage(msg),它最终还是回调
handler的handleMessage方法来处理消息的,那么也就和我们的以前了解的不谋而合了。总体上可以用下面这张图概括:(子线程发消息到主线程的处理图)
最后到这里,我们的源码就分析完毕了,但是最后要说的是还是要自己看一看源码,博客写的再详细,也不可能有源码详细,全面。