文章目錄
- 1.簡介
- 2.特性
- 3.示範
- 3.1 內建
- 3.2 建立Message
- 3.3 建立Handler
- 3.4 發送Message
- 3.5 整體代碼
- 3.6 總結
- 4.源碼位址
1.簡介
Handler,作為Android官方提供的一個專門處理消息處理的機制,嚴格來說其實并不算架構。但是為了更好地了解接下來将要學習的消息處理架構EventBus以及RxJava,我們還是需要先把官方提供的這套機制了解透徹。
Android開發者應該都了解,UI隻可以在Android的主線程(UI線程)中進行更新,若想要在子線程中更新UI,就會抛出異常,因為Android的底層在繪制UI時會調用一個名為
checkThread()
的方法,通過這個方法,Android就會知道UI更新具體是在哪個線程進行的,如果是在子線程,則會抛出異常。
如果現在有個需求,例如在某個耗時操作中提供進度條的回顯。為了解決這個需求,需要在子線程進行耗時操作的同時更新
ProgressBar
控件,但是這樣就違反了Android系統中規定的不能在子線程中更新UI的規定。為了解決這個問題,Android就提供了一個異步消息處理工具:Handler,通過Handler,我們可以收發消息,進而切換主/子線程,達到在子線程中更新UI的效果。當然,除了這個功能外,Handler最大的作用還是解決多線程并發的問題。
作為一個典型的生産者——消費者線程模型,現在網上擁有很多詳細介紹Handler的博文,而這篇博文以實用為主,介紹開發過程中與Handler相關的一些常用api和業務場景,關于理論的知識,可能會少介紹一些。如果讀者想要了解更多關于Handler的理論知識,也可參考其他更加詳細的博文。
2.特性
Android中的Handler機制主要由4個部分組成:
Message
、
Handler
、
MessageQueue
和
Looper
。其中
Message
和
Handler
在代碼中将會頻繁使用,而
MessageQueue
和
Looper
則封裝在Handler的等,下面我就對這4個部分進行一下簡要的介紹:
-
Message是線上程之間傳遞的消息,它可以在内部攜帶少量的資訊,用于在不同線程之間交換資料。我們可以使用到 Message的Message
字段來傳遞資訊,除此之外還可以使用what
和arg1
字段來攜帶一些整型資料,使用obj字段攜帶一個arg2
對象。Object
-
Handler顧名思義也就是處理者的意思,它主要是用于發送和處理消息的。發送消息一般是使用Handler的Handler
方法,而發岀的消息經過一系列地輾轉處理後,最終會傳遞到Handler的 sendMessage()
方法中。handleMessage()
-
MessageQueue是消息隊列的意思,它主要用于存放所有通過Handler發送的消息。這部分消息會一直存在于消息隊列中,等待被處理。每個線程中隻會有一個MessageQueue對象。MessageQueue
-
Looper是每個線程中的MessageQueue的管家,調用Looper的Looper
方法後,就會進入到一個無限循環當中,然後每當發現MessageQueue中存在一條消息,就會将它取出,并傳遞到Handler的loop()
方法中。每個線程中也隻會有一個Looper對象。handleMessage()
了解了
Message
、
Handler
、
MessageQueue
和以及
Looper
的基本概念後,我們再來把正常的異步消息處理的整個流程梳理一遍。
- 首先需要在主線程當中建立一個Handler對象,并重寫
方法。handleMessage()
- 然後當子線程中需要進行UI操作時,就建立一個Message對象,并通過Handler将這條消息發送出去。
- 之後這條消息會被添加到MessageQueue的隊列中等待被處理, 而Looper則會一直嘗試從MessageQueue中取岀待處理消息,最後分發回Handler的
方法中。handleMessage()
- 由于Handler是在主線程中建立的,是以此時
方法中的代碼也會在主線程中運作,于是我們在這裡就可以安心地進行UI操作了。整個異步消息處理機制的流程示意圖如圖所示:handleMessage()
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiQDOxEzX3xCZlhXam9VbsUmepNXZy9CXwJWZ3xCdh1mcvZ2Lc1zaHRGcWdUYuVzVa9GczoVdG1mWfVGc5RHLwIzX39GZhh2csATMflHLwEzX4xSZz91ZsAzMfRHLGZkRGZkRfJ3bs92YskmNhVTYykVNQJVMRhXVEF1X0hXZ0xiNx8VZ6l2cssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLwYTM2IGO2Y2N4UWNhZTMzYzX4ADN0kDM2AzLclDMyIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjLyM3Lc9CX6MHc0RHaiojIsJye.png)
一條Message經過這樣一個流程的輾轉調用後,也就從子線程進入到了主線程,從不能更新UI變成了可以更新UI,整個異步消息處理的核心思想也就是如此。
而之前曾經使用到的
runOnUiThread()
方法其實就是一個異步消息處理機制的接口封裝,它雖然表面上看起來用法更為簡單,但其實背後的實作原理和這裡的描述是一樣的。
為了便于示範,這裡隻使用Handler中的一些常用的api來實作基本功能,若想要實作額外的功能可以參考CSDN上的其他博文,這裡不再贅述。
3.示範
3.1 內建
Handler預設就是內建在Android SDK中的,是以這一步可以省略,你可以在代碼中直接使用Handler來處理消息機制。
3.2 建立Message
在使用Handler來處理消息機制時,我們應該先擷取到Message的對象執行個體。Message的建立總共有三種方式:
- 通過Message類的構造方法,即:
Message message = new Message();
- 用過Message類的
方法,即:obtain()
推薦使用這種方式來擷取Message的對象執行個體,因為Message在回收時會被放入到一個對象池中Message message = Message.obtain();
- 通過Handler類的
方法,即:obtaionMessager()
//通過handler執行個體擷取
Handler handler = new Handler();
Message message=handler.obtainMessage();
但事實上,這個方法的底層調用了第二種方式的代碼,Android源碼如下:
public final Message obtainMessage(){
return Message.obtain(this);
}
大部分的場景下,我們都是使用第二種方式來實作Message的建立。
3.3 建立Handler
建立好Message之後,接下來就是需要建立Handler。建立Handler我們需要用構造方法來擷取其執行個體,然後重寫其中的
handleMessage()
方法,表示在接收到消息後進行相應的事件處理。一個正常的Handler代碼如下:
private Handler mHandler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what){
case 1:
tv_text.setText("TextView文本在子線程中被修改了!");
break;
default:
break;
}
}
};
該Handler從
handleMessage()
裡的
msg
參數中取出
what
字段,即
msg.what
。通過該屬性值使用
Switch-Case
表達式來選擇執行相應的邏輯。
3.4 發送Message
Handler建立好後,我們就需要使用Handler來發送Message,以此來通知Handler執行相應的邏輯處理。發送Message需要借助Handler類裡的方法,其中主要的方法如下:
- 直接發送消息:
mHandler.sendMessage(message);
- 延時1s後發送消息:
mHandler.sendMessageDelayed(message,1000);
- 發送帶标記的消息(需要内部建立message,并設定
):msg.what = 0x1
mHandler.sendEmptyMessage(0x1);
- 延時1s後發送帶标記的消息(需要内部建立message,并設定
):msg.what = 0x1
mHandler.sendEmptyMessageDelayed(0x1,1);
- 移除标記為
的消息:0x1
mHandler.removeMessages(0x1);
- 移除回調的消息:
mHandler.removeCallbacks(Runnable);
- 移除回調和所有的message:
mHandler.removeCallbacksAndMessages(null);
Handler的額外知識點比較多,包括
HandlerThread
、
Hadnler避免記憶體洩漏
、
runOnUiThread()
以及Hander的
post()
方法等幾個點。這裡限于作者文筆淺陋不能很好地講解這些知識點,就暫時不寫出來了。等到作者重新組織好文思後,再來更新這篇博文。
3.5 整體代碼
為了便于示範,這裡隻有一個名為MainActivity,其Java代碼和布局檔案分别如下:
package com.androidframelearn.evnet_handler;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
private TextView tv_text;
private Button btn_send_message;
private Handler mHandler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what){
case 1:
tv_text.setText("TextView文本在子線程中被修改了!");
break;
default:
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 初始化UI
initUI();
// 為按鈕注冊點選事件
btn_send_message.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
Message message = Message.obtain();
message.what = 1;
mHandler.sendMessage(message);
}
}).start();
}
});
}
/**
* 初始化UI
*/
private void initUI() {
tv_text = findViewById(R.id.tv_text);
btn_send_message = findViewById(R.id.btn_send_message);
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<TextView
android:id="@+id/tv_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="測試用的文本!"
android:textSize="20sp"/>
<Button
android:id="@+id/btn_send_message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="發送消息"/>
</LinearLayout>
3.6 總結
- 建立Message對象,設定其
字段;what
- 建立Handler對象,重寫其
方法;handlerMessage()
- 發送Message對象,使用Handler的
方法,然後在sendMessage()
中進行相應的事件處理。handlerMessage()