1. 訊息機制
1.1 post
系列
通過檢視原始碼可知,post(Runnable r)
、postDelayed(Runnable r, long delayMillis)
最終呼叫的都是sendMessageDelayed
方法:
// post
public final boolean post(Runnable r){
return sendMessageDelayed(getPostMessage(r), 0);
}
// postDelayed
public final boolean postDelayed(Runnable r, long delayMillis){
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
複製程式碼
1.2 postAtTime
postAtTime(Runnable r, long uptimeMillis)
最終呼叫的是sendMessageAtTime
方法:
// postAtTime
public final boolean postAtTime(Runnable r, Object token, long uptimeMillis){
return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
}
複製程式碼
這裡面都有一個共同的方法getPostMessage
:
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
複製程式碼
m.callback = r
這句可以看出:getPostMessage
就是把傳入的 Runnable 賦值給 Message 物件的 callback 屬性。
1.3 sendEmptyMessage
sendEmptyMessage
最終指向sendEmptyMessageDelayed
函式:
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
複製程式碼
msg.what = what
這句可以看出:sendEmptyMessageDelayed
就是把 what 賦值給 Message 的 what 屬性。
1.4 sendMessage(msg : Message)
至於常用的sendMessage(msg : Message)
就不用細說了,這是直接傳入 Message 型別的引數。
綜合以上這幾點來說,各種傳送訊息的方法最終都是把訊息賦值給 Message 物件(或者 Message 的屬性),而且這些方法最終呼叫的都是 MessageQueue 中的
enqueueMessage
方法,就是把訊息加入訊息佇列
1.5 enqueueMessage
方法
方法較長,我們看看關鍵的幾行:
Message prev;
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;
複製程式碼
用一個無限迴圈將訊息加入到訊息佇列中(連結串列的形式),到這裡把訊息發出去並加入佇列這兩步算是完成了,接下來就是取出並處理訊息。
1.6 Looper 取出訊息
Looper 中有一個死迴圈(Looper.loop()
)用來不斷從佇列中取出訊息:
public static void loop() {
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
for (;;) {
Message msg = queue.next();
...程式碼省略
msg.target.dispatchMessage(msg);
...程式碼省略
msg.recycleUnchecked();
}
}
複製程式碼
queue.next()
每次取出一條 Message 訊息,然後交由msg.targer.dispatchMessage(msg)
處理,從上篇文章中可以知道,msg.targer
就是發出訊息的 Handler,所以我們只需要關注dispatchMessage(msg)
。
1.7 dispatchMessage(msg)
處理訊息
dispatchMessage(msg)
在 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 不為空,呼叫
handleCallback
方法(message.callback.run()) - mCallback 不為空,呼叫
mCallback.handleMessage(msg)
- 最後如果其他都為空,執行 Handler 自身的
handleMessage(msg)
方法
第 1 點就是上面的 【1.1
post
系列】 和 【1.2postAtTime
】,第 3 點就是我們最常見的handleMessage
方法。需要注意一下就是callback.run()
這裡直接呼叫執行緒的 run 方法,相當於普通方法呼叫,不會開啟新的執行緒。
現在談談第 2 點,Handler 有很多種構造方法,除了上一篇文章提到的public Handler(Looper looper)
和Handler()
等,還有一個:
public Handler(Looper looper, Callback callback) {
this(looper, callback, false);
}
複製程式碼
Callback 是這樣的:
public interface Callback {
public boolean handleMessage(Message msg);
}
複製程式碼
需要重寫handleMessage
,這不就是 Handler 裡面的handleMessage
嗎?其實兩者是有區別的:
// Handler
public void handleMessage(Message msg) {}
// Callback
public boolean handleMessage(Message msg);
複製程式碼
Callback 裡面的handleMessage
返回值是 Boolean 型別的,那麼接下來分別返回 true 和 false 看看效果吧:
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 callback = object: Handler.Callback{
override fun handleMessage(msg: Message?): Boolean {
Log.e("abc","--- Callback:threadName ---" + Thread.currentThread().name
)
return true
}
}
val thread = object : Thread() {
override fun run() {
super.run()
handler = object : Handler(looper, callback) {
override fun handleMessage(msg: Message?) {
super.handleMessage(msg)
Log.e("abc","--- handleMessage:threadName ---" + Thread.currentThread().name
)
}
}
}
}
thread.start()
myBtn.setOnClickListener {
handler?.sendEmptyMessage(4)
}
}
}
// Log 列印情況
--- Callback:threadName ---main
複製程式碼
如果返回值型別改成 false:
val callback = object: Handler.Callback{
override fun handleMessage(msg: Message?): Boolean {
Log.e("abc", "--- Callback:threadName ---" + Thread.currentThread().name
)
return false
}
}
// Log 列印情況
--- Callback:threadName ---main
--- handleMessage:threadName ---main
複製程式碼
所以,Callback 中的handleMessage
返回 true 就不繼續執行 Handler 中的handlerMessage
了,反之則兩個handleMessage
都執行。其實這些從dispatchMessage
方法中可以看出來(返回 true 則 return 終止,否則繼續執行 handleMessage):
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
複製程式碼
總結
上面主要講了訊息的傳送和取出,大概知道了 Handler 訊息機制的工作流程:
- Handler 物件通過
post
(postDelayed
、postAtTime
)或者sendMessage
(sendEmptyMessage
)把訊息(Message)交給 MessageQueue MessageQueue.enqueueMessage
方法將 Message 以連結串列的形式放入訊息佇列中Looper.loop()
迴圈呼叫 MessageQueue 的next()
取出訊息,交給 Handler 的dispatchMessage
方法處理訊息dispatchMessage()
中分別判斷msg.callback
和建構函式傳入的mCallback
是否為空,不為空則執行它們的回撥,為空則執行 Handler 自身的handlerMessage
方法。
2. Handler 記憶體洩漏問題
2.1 引起記憶體洩漏的原因
下面這樣寫會有記憶體洩漏風險:
val mHandler = object : Handler() {
override fun handleMessage(msg: Message?) {
super.handleMessage(msg)
}
}
複製程式碼
Android Studio 會標黃警告,滑鼠放在 handler 程式碼塊部分還會彈出提示,大概意思就是建議你用靜態模式或者弱引用。上面這種寫法相當於定義了一個匿名內部類,非靜態的匿名內部類預設是持有外部類(對應 Activity 等)引用的。如果發訊息的 handler 所線上程還在執行,當前 Activity 就被 finish 了,那麼該 Handler 的匿名內部類持有 Activity 的引用,所以 Activity 物件是無法被 GC 機制回收的。即:執行了 finish 程式碼,但 Activity 物件還在記憶體中(記憶體洩漏)。這種物件如果越來越多,就會有 OOM(記憶體溢位)的可能。
2.2 解決辦法
kotlin 中沒有靜態類這個概念,這裡用 java 靜態內部類舉例:
static class MyHandler extends Handler {
WeakReference<MyActivity> weakActivity;
MyHandler(MyActivity activity) {
weakActivity = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
MyActivity activity = weakActivity.get();
// activity.text = "......"
}
}
複製程式碼
靜態內部類不持有外部類引用,所以不會導致 Activity 物件洩漏(java 中 「靜態的」等於「類的」,靜態內部類如果能持有外部類引用,那說明外部類的引用就是內部「類的」,這不符合邏輯,這樣寫編譯都不通過)。
但該靜態內部類必須使用外部類的引用(比如操作 UI),此時就可以用弱引用的方式。上面程式碼用的是把 Activity 的弱引用在 Handler 建構函式中初始化,這樣如果需要操作 UI,可以使用
activity.text = "test"
這種方式。
參考文章: