大多数情况下,handleMessage
所在线程和 handler 初始化所在的线程相同,但 handler 初始化的时候可以传入一个 Looper 对象,此时handleMessage
所在线程和参数looper
所在线程相同。
1. 含参构造public Handler(Looper looper)
class MainActivity : AppCompatActivity() {
var handler: Handler? = null
var looper: Looper? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
looper = Looper.getMainLooper()
val thread = object : Thread() {
override fun run() {
super.run()
Log.e("abc", "--- Runnable:threadName ---" + Thread.currentThread().name)
handler = object : Handler(looper) {
override fun handleMessage(msg: Message?) {
super.handleMessage(msg)
Log.e("abc","--- handleMessage:threadName ---" + Thread.currentThread().name
)
}
}
}
}
thread.start()
myBtn.setOnClickListener {
val msg = Message()
handler!!.sendMessage(msg)
}
}
}
// log 打印情况
--- Runnable:threadName ---Thread-2
--- handleMessage:threadName ---main
从 log 中可以看到 handler 初始化所在线程在 Thread-2,而handleMessage
所在的线程是主线程main
.
2. 无参构造
如果使用无参的 Handler 初始化构造,需要手动调用Looper.prepare()
和Looper.loop()
:
val thread = object : Thread() {
override fun run() {
super.run()
Log.e("abc", "--- Runnable:threadName ---" + Thread.currentThread().name)
Looper.prepare()
handler = object : Handler() {
override fun handleMessage(msg: Message?) {
super.handleMessage(msg)
Log.e(
"abc", "--- handleMessage:threadName ---" + Thread.currentThread().name
)
}
}
Looper.loop()
}
}
// log 打印情况
--- Runnable:threadName ---Thread-2
--- handleMessage:threadName ---Thread-2
不手动调用Looper.prepare()
会抛出异常:
java.lang.RuntimeException: Can't create handler inside thread Thread[Thread-2,5,main] that has not called Looper.prepare()
主线程中使用 Handler:
大多数时候我们不会在子线程中初始化和使用 handler,而是在主线程中使用,此时不需要prepare()
和loop()
,因为主线程中自带一个 Looper(通过Looper.getMainLooper()
可以获取)
3. 一个线程可以有多少个 Looper?Handler 和 Looper 之间如何关联?
3.1 一个线程可以有多少个 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));
}
继续查看sThreadLocal.set(new Looper(quitAllowed))
:
static final ThreadLocal sThreadLocal = new ThreadLocal();
Threadlocal 是一个线程内部的存储类,可以在指定线程内存储数据,数据存储以后,只有指定线程可以得到存储数据。在这里 ThreadLocal 的作用是保证了每个线程都有各自的 Looper,就是说一个线程只能有一个 Looper,关于 Threadlocal,可以看看这篇文章 Threadlocal
接下来看看创建 Looper 实例的方法new Looper(quitAllowed)
:
final MessageQueue mQueue;
final Thread mThread;
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
在构造方法里,初始化 了MessageQueue 和代表当前线程的属性 mThread.
调用
Looper.prepare()
其实就是利用 ThreadLocal 为当前的线程创建了一个独立的 Looper,这其中包含了一个消息队列
3.2 Handler 和 Looper 之间如何关联
一个线程只能有一个 Looper,但一个线程中可以创建多个 Handler,那么一个 Looper 怎么和多个 Handler 对应呢?查看源码可知,post(Runnable r)
、postDelayed(Runnable r, long delayMillis)
、postAtTime(Runnable r, long uptimeMillis)
和sendMessage
最终调用的都是enqueueMessage
方法:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
msg.target = this
这里就是将当前的 Handler 赋值给 Message 对象的 target 属性,这样在处理消息的时候通过msg.target
就可以区分开不同的 Handler 了。