网上有超级多的讲述Handler原理的文章,可是还得自己重新整理一遍,这样理解才能更加深刻,毕竟binder和Handler可以算是Android的基石。
按照套路,先抛出自己的问题:
- 子线程怎么创建使用Handler?
- 主线程的Looper一直循环怎么没有导致ANR?
- 主线程的Looper在没有消息的时候会被阻塞,那么主线程不会被一直阻塞吗?
- HandlerThread是什么?以及内部是怎样的?
先不看源码分析,直接看第一个问题:
1. 子线程怎么创建使用Handler?
Looper looper = null;
Thread subThread = new Thread() {
@override
public void run() {
Looper.prepare();
looper = Looper.myLooper();
Looper.loop();
}
}
subThread.start();
Handler handler = new Handler(looper) {
@override
public void handlerMessage(Message msg) {
// 这部分处理运行subThread线程中
}
}
以上是创建在子线程的Handler的一种方法,一开始也是感到很疑惑,什么Looper.prapare()、Looper.loop()?一步一步慢慢来。
ThreadLocal
在理解Looper之前,先看下ThreadLocal,ThreadLocal不止使用起来很方便,其实原理也不难。
static ThreadLocal<Integer> mThreadLocal = new ThreadLocal<>();
// 在线程1
mThreadLocal.set(1);
int value = mThreadLocal.get(); //value: 1
// 在线程2
mThreadLocal.set(2);
int value = mThreadLocal.get(); //value: 2
// 在线程3
int value = mThreadLocal.get(); //value: null
可见同一个ThreadLocal变量在每个线程都是独立的值,相互之间并不影响;在线程1对mThreadLocal做的操作,在线程2并不可见。
ThreadLocal原理
ThreadLocal的原理用一张图就可以概括:
每个Thread内部有一个表ThreadLocalMap(类似一个简单版的HashMap),内部存储的键值对<ThreadLocal, object>
。
ThreadLocal提供set()
、get()
方法;其中set方法就是在当前线程的ThreadLocalMap插入一个<ThreadLocal, object>
,而get方法就是在当前线程的ThreadLocalMap,通过ThreadLocal本身为key,取出值object。
这样一来,同一个ThreadLocal在不同的线程存放不同的value,反过来,拿着ThreadLocal就能从不同的线程取出不同的value,因为ThreadLocal可以充当key。
下面从源码角度验证以上的理论,简要地分析
public class Thread implement Runnable{
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
}
类Thread含有两个变量threadLocals、inhreitableThreadLocals,两个都是ThreadLocalMap,区别在于后者inhreitableThreadLocals在初始化的时候会将父线程的ThreadLocals的值 给复制到新线程中的ThreadLocals。
简单看一下ThreadLocal的get()
、set()
、remove()
方法
get()
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);// 获取当前线程中的ThreadLocalMap
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);// 以本身ThreadLocal为key 从map取出value
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();// map为null,初始化
}
get() 流程:
- 获取当前线程中的ThreadLocalMap
- 以ThreadLocal本身为key,从ThreadLocalMap对应的value
set(T)
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
set() 流程:
- 获取当前线程中的ThreadLocalMap
- 以ThreadLocal本身为key,从ThreadLocalMap删除Entry
ThreadLocalMap解决Hash冲突的方式就是寻找下一个相邻的位置;
ThreadLocalMap采用的是WeakReference为key,所以当ThreadLocal被回收,key就为null,而value却是强引用,这样是不是会造成内存泄漏呢?
ThreadLocalMap是这样处理的,在get()
、set()
、remove()
等方法都会清除掉key为null的结点。
ThreadLocal可能会造成内存泄漏的原因:
- 在设置
set()
以后没有remove()
; - 在设置
set()
之后一段时间,ThreadLocal虽然已经被回收了,但是没有再get()
或者remove()
等方法,导致没有机会清除value。
Message
Message 表示消息,Handler每次发送或者处理的一个Message。
平时,我们都是这样获取一个Message:
Message msg = Message.obtain();
先看一下Message的结构
public class Message {
long when; // 表示延迟时间
Bundle data;
Handler target; // 发送和最后处理的Handler
Runnable callback;
...
Message next;
private static Message sPool;
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 50;
}
Message内部有一个链表sPool,通过next串连起来,表示Message池,最大为50个。
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
obtain()
就是在sPool池中取一个Message,否则就new一个Message。
对应的看一下recycle()回收方法,负责将Message存入sPool,留取下次使用
public void recycle() {
...
recycleUnchecked();
}
void recycleUnchecked() {
...// 清除Message资源、标记
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
MessageQueue
大致看完Message,自然就引出了MessageQueue;顾名思义,Message的队列,负责加入Message,对外提供Message。
重要的变量:
Message mMessages;
Message链表,负责储存Message
主要看两个方法:
boolean enqueueMessage(Message msg, long when); 加入Message
Message next(); 获取下一个Message
enqueueMessage()
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
// message的handler不能为空
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
// message被使用了
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
if (mQuitting) {
// MessageQueue已经被调用quit()退出了
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
msg.recycle();
return false;
}
msg.markInUse(); // 设置msg被使用了
msg.when = when; // 设置msg延迟时间
Message p = mMessages; // mMessages为Message链表头结点
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// 条件:Message链表为空,或者延迟时间为0,就是说需要马上执行, 或者延迟时间比头结点的Message的延迟时间短
// 结果:将msg设置为头结点
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
// 以下就是找出插入msg到mMessage的位置
// 根据when延迟时间来决定,延迟短的排在前面,延迟长的排在后面
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
enqueueMessage()
流程:
- 插入Message到mMessages队列中,插入位置根据延迟时间决定,延迟短的排在前面,延迟长的排在后面
- mMessages插入了消息,
nativeWake()
是native方法,会唤醒MessageQueue的next()
继续执行,这部分需要跟后面的next()
结合。
next()
Message next() {
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
// 死循环
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);// 进入阻塞,休眠nextPollTimeoutMillis时间
synchronized (this) {
// 获取当前时间
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
// 从mMessages往后找,找到一个合法的Message
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());// 这里不考虑异步消息
}
if (msg != null) {
if (now < msg.when) {
// 如果msg的时间还没到
// nextPollTimeoutMillis 是需要休眠多长时间
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
// 从mMessages删去msg
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
msg.markInUse();
return msg;// 返回
}
} else {
nextPollTimeoutMillis = -1;
}
if (mQuitting) {
// 如果MessageQueue退出了
dispose(); // 收尾
return null;// 返回null
}
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
// IdleHandler 表示 MessageQueue空闲时候的需要进行通知下IdleHandler接口
// mIdleHandlers 是 IdleHandler接口的集合,是一个观察者模式
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
pendingIdleHandlerCount = 0;
nextPollTimeoutMillis = 0;
}
}
next()
流程:
next()
方法是一个死循环,从mMessages队首中获取一个Message,如果Message到时了,则直接返回出去;- 如果队首Message还没到时了,计算一个等待时间(nextPollTimeoutMillis)并调用nativePollOnce()进入阻塞状态;
- 如果当前其他线程
enqueueMessage()
插入消息到队列头部,会调用了nativeWake(),唤醒执行当前线程继续执行next(),获取消息。
上面没有考虑同步屏障的问题,默认Message都是同步消息,但是Message可以调用setAsynchronous(true)
设置成异步消息。那么同步消息和异步消息有什么不同呢?
MessageQueue提供了postSyncBarrier()
设置同步屏障和removeSyncBarrier()
移除同步屏障,其实设置同步屏障就是插入一个target为null的Message到消息队列中,如果碰到同步屏障,那么同步屏障之后的同步消息会被阻拦,异步消息不会收到影响。这部分只做了简单的了解。
public static interface IdleHandler {
boolean queueIdle();
}
IdleHandler 表示 MessageQueue空闲时候的会回调这个接口,如果返回false,那么从mIdleHandlers移除它,返回true就会在下次空闲的时候继续回调。更多的用法参考:云图网
Looper
Looper 主要负责派发Message给Handler处理。
Looper内部有一个MessageQueue,负责存储待处理的Message。
看下怎么为当前线程生成一个Looper
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));
}
Looper有两个比较重要的方法:
prepare()生成一个Looper
loop() 不断轮询,从MessageQueue获取消息并派发消息
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));
}
Looper的prepare()
会从new一个Looper,通过sThreadLocal保存,上文也介绍了ThreadLocal的特性,这样能保证每个线程都有一个自己的Looper,不会相互影响。
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
myLooper从sThreadLocal取出,返回当前线程的Looper
最后重点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;
...
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); // 分发给Handler处理Message
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
}
...
msg.recycleUnchecked();// 回收Message
}
}
loop()
流程:
loop()
是一个死循环,不断的从MessageQueue.next()
获取消息Message;- 如果MessageQueue没有消息
next()
会将当前线程阻塞,直至有消息; - 如果MessageQueue的
next()
返回null,则说明要退出loop()循环; - 获取得到msg,调用msg.target.dispatchMessage分发,msg.target就是发送消息的Handler,也就是最后会分派给Handler处理消息;
- 处理完成,
msg.recycleUnchecked()
将Message回收,可以看上文的关于Message介绍。
Looper的退出有两个方法:quit()
、quitSafely()
// Looper.java
public void quit() {
mQueue.quit(false);
}
public void quitSafely() {
mQueue.quit(true);
}
// MessageQueue.java
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;// 设置退出标志,让MessageQueue不再接收消息
if (safe) {
// 安全退出
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// 唤醒MessageQueue
nativeWake(mPtr);
}
}
removeAllFutureMessagesLocked()
会清除MessageQueue中所有延迟的消息
removeAllMessagesLocked()
会清除MessageQueue中的消息
看Handler如何结合Looper做到切换线程处理消息。
Handler
如何构造一个Handler :
public Handler() {
this(null, false);
}
public Handler(Callback callback, boolean async) {
...
mLooper = Looper.myLooper();// 获取当前线程的Looper
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;
}
Handler调用默认构造函数,内部会绑定当前的线程的Looper和MessageQueue,如果Looper不存在会抛出异常。
这也是子线程为什么构造Handler会出错的原因。
public Handler(Looper looper) {
this(looper, null, false);
}
Handler也支持指定一个Looper。
Handler发送消息
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
Handler发送消息,最后还是走到了MessageQueue.enqueueMessage()
,往MessageQueue插入一个消息。
Handler处理消息
在Looper.loop()
中有一句分发处理消息:
// Looper.loop()
msg.target.dispatchMessage(msg);
其中target就是Handler
// Handler
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
msg.callback 是Runnable,通过post(Runnable)
转换成Message;
mCallback 是通过Handler构造函数传入的,一般是没有设置,默认为null;
最后走入了我们最熟悉的handleMessage()
,也是经常覆盖重写的处理Message的方法。
总结
- 在其他线程中,通过Handler将一个消息Message插入到消息队列MessageQueue;
2/ 在本线程中,Looper从MessageQueue获取一个Message,这个Message可能来源于其他线程; - 在线程中,最后Looper派发给Handler处理。
以上就做到了切换线程的操作。
问题解答:
-
子线程怎么创建使用Handler?
从上面的整个介绍可以看出,一个线程是否能够使用Handler,是看它有木有处于Looper的循环中,只有在Looper的循环中才可以处理Message,Handler更只是一个发送数据和处理数据挂件。
Looper looper = null;
Thread subThread = new Thread() {
@override
public void run() {
Looper.prepare();// 生成线程的Looper
looper = Looper.myLooper();
Looper.loop();// 让Looper运作起来
}
}
subThread.start();
// 发送消息和在handleMessage处理消息
Handler handler = new Handler(looper) {
@override
public void handlerMessage(Message msg) {
// 这部分处理运行subThread线程中
}
}
-
主线程的Looper一直循环怎么没有导致ANR?
Android Activity中,各种生命周期都运作在Looper,包括onCreate、onResume等
在Activity的入口,类ActivityThread可以看到
// ActivityThread
public static void main(String[] args) {
...
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
...
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
...
Looper.loop();
}
AcitivtyThread的main方法是Activity启动的入口,可以看到`Looper.prepareMainLooper()`和`Looper.loop()`,而sMainThreadHandler是Handler。
所以反过来说,之所以会发送ANR,是因为Looper在循环处理消息的过程中,某一个消息处理时间太长了,导致发生ANR,例如在主线程中做网络操作。
-
主线程的Looper在没有消息的时候会被阻塞,那么主线程不会被一直阻塞吗?
确实,从上面的代码可以看出,当Looper在next()时如果没有消息处理,会发生阻塞,但是一旦有消息MessageQueue.enqueueMessage()也会唤醒,使得Looper继续处理消息
-
HandlerThread是什么?以及内部是怎样的?
HandlerThread是一个内部具有Looper的线程Thread,直接看下代码:
public class HandlerThread extends Thread {
Looper mLooper;
private @Nullable Handler mHandler;
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
public Handler getThreadHandler() {
if (mHandler == null) {
mHandler = new Handler(getLooper());
}
return mHandler;
}
}
可见,HandlerThread就是一个问题1的答案,在run()进行loop()开启消息循环,外部可以通过getThreadHandler()获取Handler,发送消息,最后在HandlerThread所在的子线程中处理消息。
在Android中,一切皆为消息。
原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/6270.html