大多數情況下,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<Looper> sThreadLocal = new ThreadLocal<Looper>();
複製程式碼
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 了。