【Android源码分析】Threadlocal与looper+handler
2018-01-12 23:37
309 查看
前言
为什么会有这么一篇网上有很多种解说版本的博客?因为我看懂了很多次,都没有把自己的想法记下来,然后就忘了。那样不仅浪费时间、而且还有点伤积极性。从一个异常出发开始
在《第一行代码》中看到了关于异步处理消息的用法时,有没有想过可以在子线程中去new一个Handler?现在就开始着手,从一个子线程中去new一个Handler,看看会有什么发生。new Thread(new Runnable() { @Override public void run() { new Handler(); } }).start();结果就出现了RuntimeException异常,仔细看它的信息说明。
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()那么可知,在每个线程
new Handler()时,都必须先调用
Looper.prepare()或者调用一个能够达到相同效果的函数。那么在主线程中可以
new Handler()的原因,想必就是已经调用过了。以下代码位于AcitivityThread.java中,是一段初始化主线程的内容。
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();其它的都忽略,就看Looper相关的。
Looper.prepareMainLooper()想必是达到了相同的效果吧。那么,这个效果到底是什么呢?让我们慢慢拨开云雾。
寻找那个异常
于是,我们很自然地在子线程中加入了Looper.prepare(),并随手按着Ctrl,左键点击鼠标,进入了
prepare()函数中。
/* 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)); } ... private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }当初的那个异常不就在眼前?但是,这
sThreadLocal又是什么?它是什么暂时抛开,这时我们知道了我们的线程中已经有了一个
Looper,并且为这个Looper设置好了一个MessageQueue。因为一个线程只能有一个
Looper,所以一个Looper也就只能拥有一个MessageQueue。但是AcitivityThread中,经过
Looper.loop()后就再也没有下文了?所以,这个
loop()又是干啥的呢?
繁忙的loop()
/** Return the Looper object associated with the current thread. Returns null if the calling thread is not associated with a Looper.*/ public static @Nullable Looper myLooper() { return sThreadLocal.get(); } /** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the loop. */ 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); end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis(); } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } ... msg.recycleUnchecked(); } }因为其中一个大大的死循环,所以调用了
loop()之后,其后就没有实际代码了。这个死循环就是用来处理Message,不断地从队列中取,然后不断地进行分发到相应的Handler,进行处理。此时,这个
for(;;)所处的线程,就是你调用Looper.loop()时所在的线程。因此,它分发msg给了相应的Handler的handleMessage之后,还是在此线程中执行。然后,在想想,发送Message时所处在的线程,就焕然大悟这个异步操作了。
/** * Handle system messages here. */ 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时候,所覆盖的方法吗?进一步思考,如果我只在主线程中new Handler,那么Looper就是主线程,所有的msg都会在主线程中被处理;那如果我想让msg在子线程中被处理呢?当然可以
Looper.prepare()巴拉巴拉,然后
Looper.loop()。但是Android还为我们提供了一个更为便捷的封装。那就是
HandlerThread。
子线程处理msg的封装HandlerThread
@Override public void run() { mTid = Process.myTid(); Looper.prepare(); synchronized (this) { mLooper = Looper.myLooper(); notifyAll(); } Process.setThreadPriority(mPriority); onLooperPrepared(); Looper.loop(); mTid = -1; }源代码比较短。它继承自Thread,并在run方法中初始化好了Looper,可以通过其
getThreadHandler()方法,获取到与该Looper所绑定的Handler,然后sendMessage(),最后在该线程中处理msg。
小结
因此,一个Thread可以有一个Looper和一个MessageQueue,一个Looper却可以与多个Handler绑定,但是一个Handler只能与一个Looper绑定。原因可以从Handler的构造方法中寻找的。ThreadLocal是什么
回过头,想想ThreadLocal实现了什么样的功能。举个例子,当不同的线程都去执行同样一个语句以获得当前线程的Looper时,要怎么实现?或许吧,ThreadLocal就实现了这样一个功能。在Looper中,申明了一个如下的静态变量,说明只有一个。static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();然后在Looper中调用myLooper()时,都执行了
public static @Nullable Looper myLooper() { return sThreadLocal.get(); }顺手打开,看到了这个。
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中,有个
ThreadLocal.ThreadLocalMap。获取到当前线程的map之后,又将
sThreadLocal自身作为一个Key,去获取到相应的结果。再看
set()
public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }相同的套路,同样是先获取到当前线程的map,然后用ThreadLocal自身作为Key,存进去。
小结
因此对于ThreadLocal,可以这样理解:它自身就是一个Key,然后可以以这个Key在不同的线程中存储/获取一个值,值得类型就是声明时,尖括号里面的类型。相关文章推荐
- (消息处理机制)Android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- Android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图文+源码分析)—Looper/Handler/Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- [转] android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- Android中的Looper , Handler , Message的关系,异步消息处理的机制,根据源码分析
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- 【Android消息处理机制】android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- (转)android的消息处理机制(图+源码分析)——Looper,Handler,Message