什么是Looper
android源码上Looper类的注释为:
- 用于为线程运行消息循环的类,在默认情况下线程是没有与其关联的消息循环的;可以通过在线程中调用Looper.prepare()创建一个与线程绑定的消息循环,让其处理消息,直到循环停止。
- 大多数情况下与Looper(消息循环) 的交互都是通过 Handler类进行的
- 下述代码展示了一个典型的线程创建Looper的实例:
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
//处理传入进来的消息
}
}
Looper.loop();
}
}
Looper作用
- 此类用于准备线程的消息循环类(prepare(boolean quitAllowed)) 、开始消息循环(loop())、消息循环的退出(quit() /quitSafely())。
Looper源码解析
在Looper作用中提到了其三个作用和对应的方法,下面分别来看这些方法的实现:
-
prepare()
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) );
}
上述代码调用了 ThreadLocal.set() 保存了Looper实例
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
Looper的构造方法中对MessageQueue和Thread进行了实例化。
-
loop()
/**
* 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);
}
msg.recycleUnchecked();
}
}
上述代码是经过删减留下的主要逻辑代码,其中可以看到:
- 获取当前的Looper实例进行非空判断。
- 获取当前Looper中的MessageQueue。
- 通过一个死循环调用queue.next方法取出Message
- 当Message为空时表明MessageQueue正在退出,则跳出循环
- 否则继续往下通过Message.target获取Handler,调用其dispatchMessage将Message分发给Handler处理
- 最后调用Message的 recycleUnchecked() 方法将其内容清除并放入消息池中循环使用(节省了Message创建以及销毁的时间,并提高了内存的使用率);
-
quit() /quitSafely()
/**
* Quits the looper.
* <p>
* Causes the {@link #loop} method to terminate without processing any
* more messages in the message queue.
* </p><p>
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
* </p><p class="note">
* Using this method may be unsafe because some messages may not be delivered
* before the looper terminates. Consider using {@link #quitSafely} instead to ensure
* that all pending work is completed in an orderly manner.
* </p>
*
* @see #quitSafely
*/
public void quit() {
mQueue.quit(false);
}
/**
* Quits the looper safely.
* <p>
* Causes the {@link #loop} method to terminate as soon as all remaining messages
* in the message queue that are already due to be delivered have been handled.
* However pending delayed messages with due times in the future will not be
* delivered before the loop terminates.
* </p><p>
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
* </p>
*/
public void quitSafely() {
mQueue.quit(true);
}
上述quit()方法会将消息队列中的所有消息移除;而quitSafely()则是将消息队列中所有的延迟消息移除,及时消息继续处理结束后再退出。上述两个方法在调用过后,再调用Handler的sendMessage则会返回false,也就是不能再发送消息了。
-
myLooper()
/**
* 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();
}
上述方法通过ThreadLocal获取当前线程的Looper。
-
ThreadLocal
在perpare() 中我们看到调用了ThreadLocal的set方法保存了当前线程的Looper实例,下面展示set的代码
/**
* Sets the current thread`s copy of this thread-local variable
* to the specified value. Most subclasses will have no need to
* override this method, relying solely on the {@link #initialValue}
* method to set the values of thread-locals.
*
* @param value the value to be stored in the current thread`s copy of
* this thread-local.
*/
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
可以看出会先获取当前线程的ThreadLocalMap(此类为ThreadLocal的静态内部类),然后根据判断结果通过不同的方法保存value值;下面展示ThreadLocalMap的set方法
/**
* 该数组根据需要调整大小.
* 该数组长度总是为2的幂数.
*/
private Entry[] table;
private void set(ThreadLocal<?> key, Object value) {
// We don`t use a fast path as with get() because it is at
// least as common to use set() to create new entries as
// it is to replace existing ones, in which case, a fast
// path would fail more often than not.
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
if (k == key) {
e.value = value;
return;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
- 首先循环Entry数组(Entry为继承弱引用的类)
- 判断是否有与key相等的值,若有则替换value值并返回;
- 判断是k值是非为空,若为空则将其替换为key并返回
- new个新的Entry值赋给tab[i]
- 上述Entry结构为
static class Entry extends WeakReference<ThreadLocal<?>> {
/**
* The value associated with this ThreadLocal.
*/
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
在myLooper() 中我们看到调用了ThreadLocal的get方法获取了当前线程的Looper实例,下面展示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();
}
可以看出与set()方法类似首先会取出当前线程的ThreadLocalMap,然后将匹配的Entry取出,通过之前代码我们知道Looper的实例赋值给了value的,因此e.value的值即为当前线程的Looper。
总结
- 阅读Looper源码得知,我们可以通过Looper与Handler的组合让线程一直保持着待命的状态,当有消息时能及时处理消息。
- looper是存储在当前线程的ThreadLocalMap中的,ThreadLocal的get和set方法都是操作当前线程的ThreadLocalMap的,因此在不同的线程中调用ThreadLocal的get和set方法操作的对象是不同的。