1 为什么以这一个点为开头?
因为面试的时候被问到ThreadLocal完全不懂,前几天发现Looper内正好使用了ThreadLocal,那么从哪里跌倒就从哪里爬起来。
2 什么是Looper
首先看/sdk/docs/reference/android/os/Looper.html内的定义
Class used to run a message loop for a thread. Threads by default do not have a message loop associated with them; to create one, call prepare() in the thread that is to run the loop, and then loop() to have it process messages until the loop is stopped.
Looper类用于在一个线程中运行一个消息循环。默认的线程不包含消息循环。
如果需要创建一个Looper,在Thread的run函数中调用Looper.prepare()
接着调用Thread.loop()函数,直到消息队列停止。
简单的说,Looper就是一个消息循环,在一个线程中不停的去消息队列里poll新消息出来给Handler处理,
3 如何创建Looper
import android.os.Handler;import android.os.Looper;import android.os.Message;import android.support.v7.app.ActionBarActivity;import android.os.Bundle;import android.util.Log;public class MainActivity extends ActionBarActivity { private static final String TAG = "MainActivity"; private class LooperThread extends Thread { public Handler mHandler; @Override public void run() { Looper.prepare(); mHandler = new Handler() { @Override public void handleMessage(Message msg) { Log.i(TAG,msg.toString()); } }; Looper.loop(); } } private LooperThread mLooperThread = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mLooperThread = new LooperThread(); mLooperThread.start(); }}
这是reference中给出的子线程中Looper的构建方法
a 创建新线程,重写run函数
b 调用Looper.prepare()
c 创建Handler
d 调用Looper.loop()
我们下面分4部分解读源代码
3.1 Looper概览
- 成员变量
public final class Looper { private static final String TAG = "Looper"; // sThreadLocal.get() will return null unless you've called prepare(). static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); private static Looper sMainLooper; // guarded by Looper.class final MessageQueue mQueue; final Thread mThread; private Printer mLogging;}
Looper拥有6个成员变量,其中mLogging和Tag先忽略
sMainLooper、ThreadLocal为static成员
mQueue、mThread为实例的成员变量
- 成员函数
Public Methods | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
Returns the application's main looper, which lives in the main thread of the application. | |||||||||||
Return the Thread associated with this Looper. | |||||||||||
Run the message queue in this thread. | |||||||||||
Return the Looper object associated with the current thread. | |||||||||||
Return the MessageQueue object associated with the current thread. | |||||||||||
Initialize the current thread as a looper. | |||||||||||
Initialize the current thread as a looper, marking it as an application's main looper. | |||||||||||
Quits the looper. | |||||||||||
Quits the looper safely. | |||||||||||
Control logging of messages as they are processed by this Looper. | |||||||||||
Returns a string containing a concise, human-readable description of this object. |
3.2 调用Looper.prepare()
我们先看看Looper.prepare()定义
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));}从静态的prepare函数可以每一个Thread只能对应一个Looper,不然会报RuntimeException
而prepare函数所做的事情只是在全局的sThreadLocal中存放了个新的Looper
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }new Looper时赋值了mQueue和mThread
既然sThreadLocal是个全局的静态变量,那么所有的Looper类都共享同一个sThreadLocal
sThreadLocal.set操作定义如下
java.lang.ThreadLocal.java
public void set(T value) { Thread currentThread = Thread.currentThread(); Values values = values(currentThread); if (values == null) { values = initializeValues(currentThread); } values.put(this, value); }即从当前调用线程中取出values对象,然后往这个values对象存放这个Looper
需要注意的是每个Thread中都有一个values对象,
这个values对象再按照ThreadLocal<Looper> sThreadLocal对象在当前线程的values哈希表中找出对应的Looper
那么这个Looper就对应为当前线程的Looper
那么使用ThreadLocal有什么好处呢?
好处是显而易见的,如果用全局的HashMap管理一个Thread对应一个Looper,
那么增删改某个Looper对象时就需要进行同步操作,这大大增加了系统开销
而如果有一个ThreadLocal.Values对象存放在Thread里,需要用到时就直接获取,不与其他线程的数据进行交互,
那么就避免了同步带来的低效率问题,所以这个ThreadLocal正好被应用到了一个Thread对应一个Looper中
3.3 创建Handler
android.os.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;}
因篇(lan)幅(de)限(xie)制(le),Handler只提到1个函数,在创建Handler时无参数代入,即调用到Handler(null,false)
在下面一个函数中handler进行了一些初始化的赋值,最重要的一步是赋值了mLooper和mQueue
Looper.myLooper()函数定义如下
public static Looper myLooper() { return sThreadLocal.get();}
即从sThreadLocal中获取了当前Thread中得looper,赋值给了Handler,
这也就解释了为什么需要先Looper.prepare()
再new Handler的原因了
3.4 调用Looper.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; // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } // This must be in a local variable, in case a UI event sets the logger Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } msg.target.dispatchMessage(msg); if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } // Make sure that during the course of dispatching the // identity of the thread wasn't corrupted. final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) { Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what); } msg.recycle(); }}因为是静态函数,需要先获得当前线程的looper实例,
接着调用Binder.clearCallingIdentity(),这个函数返回的是当前进程的pid、uid等信息混合后的唯一标示
接着一个for不断循环获取消息队列中得下一条消息,
如果调用的queue.next返回的msg为null则立即结束此线程
如果不为null则调用msg.target.dispatchMessage(此target为handler实例,即在当前线程调用到了handler的handlerMessage)
接着再获取一次Binder.clearCallingIdentity(),判断前后两次的唯一标示是否相同
我很奇怪既然在同一个线程为什么会需要这样的判断,后来查看Log.wtf定义发现注释如下:
What a Terrible Failure: Report a condition that should never happen.
也就是永远不会发生的情况发生了做一个记录,
因为uid是安装完app就已经生成了,pid是在程序跑起来时也生成了,如果这两个其中一个改变,
说明app被kill后重启或者重新安装,既然被kill后就不应该存在这个handler,
所以理论上应该不会发生这个情况
最后调用msg.recycle()对msg进行一个重置回收
我们来看看
android.os.Message.java的recycle函数实现
public void recycle() { clearForRecycle(); synchronized (sPoolSync) { if (sPoolSize < MAX_POOL_SIZE) { next = sPool; sPool = this; sPoolSize++; } }}正如代码中所示,首先把所有消息内容清空,然后放入对象池提供给其他对象使用
4 最佳实践
既然上面的Message使用了对象池,
那么我们在使用Message msg = new Message()就太瞎了
在需要使用新的Message时,使用Message.obtain()或者Handler.obtainMessage()是个不错的选择!
5 总结
本文只是简单的叙述了Looper的构建过程,但其实内部中mQueue.next()是夸线程调用的最重要体现,mQueue是如何把其他线程上的msg dispatch给当前线程的Handler,我将在下一讲中给出解释,顺便具体学习下MessageQueue,也一定会讲到MessageQueue cpp层的一部分源代码。