参考资料:
ThreadLocal:https://www.cnblogs.com/coshaho/p/5127135.html
Handler:https://www.jianshu.com/p/3d8f7ec1017a
0.前绪
通过Handler发送的消息,会先存到一个消息列表中,就是MessageQueue这个消息列表里。Looper这个类会循环不停地从这个消息列表中按顺序取消息,取到消息后,进行消息处理(使用handleMessage方法)。很简单一个原理,实现了消息的顺序处理。
Handler涉及类包括:Handler.java、Looper.java、MessageQueue.java、ThreadLocal.java、ThreadLocalMap.java
1.源码分析
1.创建Handler
handler翻译中文是操作者,顾名思义,Handler的作用就是用来处理消息的。
Handler mHandler = new Handler();
Handler mHandler = new handler() {
public void handleMessage(Message msg){
// handle message
}
};
最常见的两种创建方式,我们常会在Activity中这样创建使用,一般用于在主线程处理任务。
看构造方法的源码,最后会跑这个方法:
Handler.java
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 " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
第11行,通过 Looper.myLooper() 获取到了Looper。
注意,此代码高能,信息量巨大,下面,进入这行代码!:
Looper.java
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
就一行代码,你以为我骗人?no,no,no。
这个方法是Looper类中的一个静态方法,这行代码中包含了重要角色: ThredLocal
这可是Looper线程绑定的精髓(严肃脸?)。
啥是线程绑定???
就是一个线程只有一个Looper对象
而用的消息队列呢,是Looper的一个内部变量,所以,一个线程也只有一个消息队列。
2.引入Looper
怎么突然从Handler的话题转移到了Looper?
我们之前说过,Looper会循环不停地从消息队列里按顺序取消息。而这个消息队列就是Looper的小弟(一个内部变量),拿到这个Looper,我们就可以往它的消息队列里放消息啦。
看看我们平时是怎么用Handler发消息的:
Message msg = Message.obtain(); msg.what = args1; msg.obj = args2; mHander.sendMessage(msg);
其实这个sendMessage方法就是起到把消息扔给消息队列的作用。这个Looper很重要吧。
先看看Looper代码中的 sThreadLocal在哪定义的
Looper.java
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
泛型参数是Looper的一个ThreadLocal静态对象。
回去看 sThreadLocal.get() 方法,这是ThreadLocal的一个方法
ThreadLocal.java
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();
}
分析一波上面代码:
先获取到了当前运行的线程
Thread t = Thread.currentThread();
通过getMap传入当前线程,获取到一个ThreadLocalMap类型的Map。
ThreadLocalMap又是啥?
从命名来理解,就是一个存ThreadLocal的Map啦,Map就是使用key-value存储形式的数据结构啦。
通过map.getEntry传入这个ThreadLocal对象获取到Entry(保存一对key-value的一个实体类),通过这个Entry的value拿到了Looper。
ThreadLocal.java
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
getMap 也是一行代码,够简单的。
它返回了线程的一个变量??
没错,我们平时创建线程用的Thread类,它就是有这么个变量。
3.引入ThreadLocalMap
Thread.java
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
注释说(个人翻译的,不知道准不准确,我就假设准备?):ThreadLocal的values值与此线程有关,这个Map由ThreadLocal来维护。
从翻译知道:Thread告诉我们,这个Map是跟我有关的,但是我不管了!让ThreadLocal自己维护!
what?搞什么飞机,ThreadLocal怎么维护???这可是你自己的东西。
好吧,我们看看ThreadLocal怎么维护。先看看ThreadLocal里的set方法
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);
}
分析下这段代码:
ThreadLocal对象拿到当前线程,然后,
还记得getMap方法吗
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
通过getMap方法拿到这个线程的 ThreadLocalMap ,如果map不是null,就把自己(ThreadLocal对象)作为key, 传入的value作为value,这对key-value放到map里。如果map是null,就创建一个新的Map,然后把key-value放进去。
对我们Looper来说,value值就是Looper对象。
Thread真够懒的,自己的变量自己不维护,让ThreadLocal自己用set、get方法搞定了。
当然了,ThreadLocal处理的线程那也是有要求的:当前运行的线程。即,谁搞我(调用我的方法),我搞谁。
通过上面一系列麻烦的操作,Looper成功地让和当前线程进行了绑定处理:让当前线程的ThreadLocalMap中含有了键值对:
------------------------------------------------
key - value |
ThreadLocal(Looper的内部变量) - Looper |
------------------------------------------------
等等,好像哪里不对,创建Handler的时候,Looper根本没有让ThreadLocal运行过set方法啊?
没有绑定过线程啊,Looper.myLooper() 不就拿不到Looper吗?
其实,主线程的Looper,在Android环境初始化的时候已经创建好咯。
Looper.java
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方法的注释:
应用程序的Looper是由Android环境创建的,因此您无需自己调用此函数。
至此就分析完辽。
划重点总结:
Thread有一个自己不想管的Map叫ThreadLocalMap,它的这个ThreadLocalMap是让ThreadLocal来管(Thread在放权啊?)。
ThreadLocal他就把自己作为键,把想保存的东西作为值,保存了到了调用他的线程的Map中。这样,键和值就和当前线程建立了关系。
课后习题:
同一个Activity主线程中可以创建多个Handler吗?
答案:当然可以,同一个Activity中的Handler们,拿到的都是主线程的Looper喔,用的同一个消息队列。
附(旧的总结):
总结0.ThreadLocal和Thread的关系
每个Thread中都有一个存储ThreadLocal的Map对象,这个Map不是使用的集合类中的Map,用的是一个叫做“ThreadLocalMap”(从名字可以明显看出来它的作用了)的类来存储。ThreadLocalMap是一个可以存储key-value的map,key就是ThreadLocal,value就是要存储的跟ThreadLocal对应的值。
这里的ThreadLocalMap是ThreadLocal的“基友类”。
下图看下两者关系
总结1.线程绑定
问起Handler的原理,会提起Looper和MessageQueue跟线程绑定,balabala。
这里实现线程绑定就是使用的Thread中的ThreadLocalMap来实现。
如何使用ThreadLocal呢。首先需要一个Manager类来持有一个ThreadLocal<T>的实例。不同的Thread将这个实例和想存储的值保存起来就好咯。ThreadLocal提供了set、get等等方法来方便获取或者保存当前运行线程(Thread.currentThread()可以获取当前线程)对应的存储值。这样为了不同的功能,我们可以定义不同的ThreadLocal,而且互不干扰。
线程绑定,直接写成Map<Thread, value>不就可以实现吗,为什么要写得如此复杂。
这种存储结构的好处:
1、线程死去的时候,线程共享变量ThreadLocalMap则销毁。
2、ThreadLocalMap<ThreadLocal,Object>键值对数量为ThreadLocal的数量,一般来说ThreadLocal数量很少,相比在ThreadLocal中用Map<Thread, Object>键值对存储线程共享变量(Thread数量一般来说比ThreadLocal数量多),性能提高很多。
2.Handler
Hander有一个相关类为Looper,这个就是上面所说的Manager类。
Looper有几个属性值:
// 存储Looper的ThreadLocal,可以看出Looper不仅充当Manager,还作为基础数据类(存储MessageQueue等信息)
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
// 主线程的Looper
private static Looper sMainLooper;
// Looper对应的MessageQueue
final MessageQueue mQueue;
可以看出Looper不仅充当Manager,还作为基础数据类(存储MessageQueue等信息),通过sThreadLocal,实现了线程和Looper的绑定。
Looper有个重要方法:
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
由此,大部分Looper的代码基本上可以知道是什么意思了。