我们经常会用到 HandlerThread 在线程中执行Runnable方法,调用代码如下,
private HandlerThread mHandlerThread = new HandlerThread("MainActivity");
mHandlerThread.start();
mWorkHandler = new Handler(mHandlerThread.getLooper());
mWorkHandler.post(new Runnable() {
@Override
public void run() {
}
});
相关机制是怎么触发的呢?
这里面涉及到HandlerThread,Looper,ThreadLocal,Handler,MessageQueue,Message 等概念,
Looper类,是用来封装消息循环和消息队列的。
流程如下:
1.mHandlerThread.start();
本身继承Thread,该方法启动线程,
HandlerThread run方法
HandlerThread.run相关代码如下:
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
1.1 执行 Looper.prepare()
Looper.prepare() 创建Looper实例放在 sThreadLocal 对象里面, 调用如下:
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));
}
1.2 Looper.myLooper()方法从 sThreadLocal 中获取Looper实例(这个实例是线程独立的,即获取的是对应线程创建的Looper实例)
给 HandlerThread 的全局变量 mLooper
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
这里sThreadLocal.get() 可以获取的对应线程创建的Looper实例原因:
/libcore/ojluni/src/main/java/java/lang/ThreadLocal.java
ThreadLocal.ThreadLocalMap 是 ThreadLocal 类的核心实现,用于存储线程的私有数据,确保线程隔离性。
/libcore/ojluni/src/main/java/java/lang/Thread.java
Thread.threadLocals 为 ThreadLocal.ThreadLocalMap对象,是在当前线程第一次调用sThreadLocal.set(new Looper(quitAllowed))时候创建ThreadLocalMap对象并赋值给Thread.threadLocals变量的。
#1.ThreadLocal.set(T value)调用流程如下:
如果是线程1第一次调用sThreadLocal.set(new Looper(quitAllowed))
ThreadLocal.set(T value) 先获取当前线程t = Thread.currentThread(),如果线程1的t.threadLocals 变量为空,基于应用创建的ThreadLocal对象 new ThreadLocalMap(sThreadLocal, value)
如果是线程1第一次调用sThreadLocal2.set(new Looper(quitAllowed))
ThreadLocal.set(T value) 先获取当前线程t = Thread.currentThread(),此时线程1的t.threadLocals 变量不为空,获取当前线程的ThreadLocalMap对象map,调用map.set(sThreadLocal2, value)
即线程1 t.threadLocals(ThreadLocalMap对象) 存储了线程1创建的所有ThreadLocal对象。
同理每个线程 调用ThreadLocal.set,都会将对应线程创建的所有ThreadLocal对象 存储到 t.threadLocals中。
#2.ThreadLocal.get()调用流程如下:
如果是线程1第一次调用sThreadLocal.get()
ThreadLocal.get() 获取当前线程的t.threadLocals(ThreadLocalMap对象) 到变量map, 然后调用map.getEntry(sThreadLocal) 获取当前 ThreadLocal对象sThreadLocal对应的值返回。
总结:
因为ThreadLocal.set(T value) 将value设置到当前调用线程Thread.threadLocals(ThreadLocalMap对象) 中,
同时ThreadLocal.get() 获取的是当前调用线程Thread.threadLocals(ThreadLocalMap对象) 对应key为sThreadLocal的值,所以ThreadLocal实例sThreadLocal是线程独立。
1.3 执行Looper.loop()创建消息循环,
核心逻辑是从 当前线程的Looper中取出 MessageQueue,循环遍历 MessageQueue 中的消息
Looper.loop()代码
public static void loop() {
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
...
}
2.new Handler(mHandlerThread.getLooper())
2.1 mHandlerThread.getLooper() 获取HandlerThread 中创建的 Looper对象
2.2 把获取的Looper初始化到Hander中
3. mWorkHandler.post(new Runnable()..)
3.1通过 getPostMessage 方法将Runnable封装到 Message 对象中
3.2 最终会调用sendMessageAtTime方法
该方法中MessageQueue对象 mQueue 就是从2.1 Hander中Lopper中获取的,而且这个MessageQueue 在1.3 消息循环中不停的被遍历,
你只要向 MessageQueue 中添加Runnable封装 Message,就会在指定的时间执行 Runnable方法。
Handler.java 相关调用代码如下:
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
----------------------
所以如下事例中在直接创建的线程中,不能直接 使用 Handler否则会抛出异常。
而HandlerThread 可以直接使用,因为HandlerThread 初始化时候内部已经调用过
Looper.prepare()
new Thread(() -> {
Log.i(TAG, "testLooper()");
//Looper.prepare();
//在线程中使用Handler 会抛出如下异常,需要在使用Handler前,调用 Looper.prepare();
//java.lang.RuntimeException: Can't create handler inside thread Thread[Thread-3,5,main] that has not called Looper.prepare()
Handler handler = new Handler();
}).start();
文章详细解析了如何在Android中使用HandlerThread执行Runnable,涉及HandlerThread启动、Looper的创建与消息循环、以及如何正确使用Handler和MessageQueue。特别强调了直接在非HandlerThread中使用Handler会抛出异常的原因。
395

被折叠的 条评论
为什么被折叠?



