如何使用Handler(kotlin)

如何使用Handler(kotlin)

情境

在 Android 使用執行緒要非常的小心,使用者在進行操作時,假設執行緒也在進行大量運算,那麼就會造成使用者畫面卡死不動,這樣的使用者體驗是不好的。

說明 + 程式碼

Android 的 Main Thread 用來處理 UI 相關執行,因此,如果想讓使用者在操作 App 感到順暢,一般來說,會將非同步的運算丟給 Worker Thread 執行,運算結束後,則會透過 Main Thread 去更新 UI,Handler 可以讓你輕易切換 Main Thread 以及 Worker Thread。
一般要了解 Handler 則會需要了解以下四個類別

  • Handler
  • Looper
  • Message
  • MessageQueue
    下面將一一說明。

Looper

一個 Thread 只能有一個 Looper,當 Message 處理完畢後,會將 Message 發送給 Handler。

Handler

一個 Thread 可以有多個 Handler,負責將 Message 送往 MessageQueue,並且接收Looper丟出來的Message。

MessageQueue

一個 Thread 只能有一個 MessageQueue,負責裝載Message的佇列, 對Message進行管理, 是一個無限制的鏈結串列。

Message

執行緒上要處理的訊息。


如上圖,Handler 負責派送訊息,交給 MessageQueue 進行排隊,再透過 Looper 將每一個 Message Object 丟給 Handler 處理。
也許上面這些東西會有點陌生,是因為 Android Main Thread 一開始就先幫你綁定好,你不需要自訂初始化 Looper 並且綁定 Handler,下面的做法都是開啟 Thread 運算完後,去呼叫 Main Thread 進行畫面更新。

Thread{  
 //這邊是背景thread在運作, 這邊可以處理比較長時間或大量的運算
 runOnUiThread{  
  //這邊是呼叫main thread handler幫我們處理UI部分    
 }  
}.start()
//或者
view.post {  
 //更新畫面
}
//又或者另外一種寫法
private val hanlder = Handler {  
 val b = when (it.what) {  
  1 -> true  
  else -> false  
 }  
 b  
}

Thread{  
 val msg = Message()  
 msg.what = 1  
 hanlder.sendMessage(msg)  
}

第一種寫法是實作 Runnable 介面讓 Main Thread 的 Handler 進行 Callback (請參考android的消息处理机制)
一般是用來處理大量數據或者長時間運算,最後再利用 Activity 內建的方法 runOnUiThread 呼叫 main thread handler 幫忙處理 UI 部分。
第二種寫法是自己定義一個 Message 物件透過 Hanlder 去進行處理。
你也可以拿 Main Thread 的 Handler 來處理比較少量資料。

Handler(Looper.getMainLooper()).post {  
 //處理少量資訊或UI  
}  

甚至你可以使用 Delay 的方法來延後 Thread 處理。

Handler(Looper.getMainLooper()).postDelayed({  
 //處理少量資訊或UI  
}, 3000)

上面是在 3 秒後處理少量資訊。
以上都是利用 MainThread 上面的 Looper 進行處理,實際上你也可以自己定義自己的 Looper。

Thread {
 Log.e(TAG, "A")
 Looper.prepare()
 Handler().post{
  Log.e(TAG, "B1")
 }
 Handler().post{
  Log.e(TAG, "B2")
  Looper.myLooper()?.quit()
 }
 Looper.loop()
 Log.e(TAG, "C")
 runOnUiThread {
  Log.e(TAG, "D")
 }
).start()

輸出為 A、B1、B2、C、D,由上面的程式碼可以看到我們自己定義的 Looper,對於 Thread 內的 Handler 會變成跟自己定義的 Looper 進行綁定,也就是說這邊是屬於 Background Thread 部分不行拿來更新 UI,而直到呼叫 Looper 內的 quit,則會將該 Looper 以及 MessageQueue 移除,Thread 才會繼續往下執行。

  • 注意: 假設這邊如果還有Message正在處理, 則會將該Message處理完畢
    再將後面未處理的Message全部移除

如果要更新 UI 則會再透過 Main Thread 的 Handler 去處理 UI 部分,當然你也可以使用 HandlerThread
講了那麼多訊息,訊息究竟是一個怎樣的物件?

android.os.Message

只要是透過 Handler 派送的訊息最後都會被包成 Message,送進 MessageQueue 等待派送,每一個 Message 都會認得把自己送進來的 Handler。

Message.obtain(handler, runnable).sendToTarget()

Message有幾種參數。

參數名稱    型別         用途
what       int         標記識別符號
obj        Object      物件, 必須Parcelable
data       Bundle      Bundle物件
callback   Runnable    實作Runnable的callback

以上是比較常用的,還有一些請參考 Android 官網