Android多執行緒:HandlerThread詳細使用手冊
阿新 • • 發佈:2018-12-16
多執行緒的應用在
Android
開發中是非常常見的,常用方法主要有:
- 繼承Thread類
- 實現Runnable介面
- Handler
- AsyncTask
- HandlerThread
今天,我將全面解析多執行緒其中一種常見用法:HandlerThread
目錄
1. 定義
一個Android
已封裝好的輕量級非同步類
2. 作用
- 實現多執行緒 在工作執行緒中執行任務,如 耗時任務
- 非同步通訊、訊息傳遞
實現工作執行緒 & 主執行緒(
UI
執行緒)之間的通訊,即:將工作執行緒的執行結果傳遞給主執行緒,從而在主執行緒中執行相關的UI
操作
從而保證執行緒安全
3. 優點
方便實現非同步通訊,即不需使用 “任務執行緒(如繼承Thread
類) + Handler
”的複雜組合
實際上,HandlerThread本質上是通過繼承Thread類和封裝Handler類的使用,從而使得建立新執行緒和與其他執行緒進行通訊變得更加方便易用
4. 工作原理
內部原理 = Thread
類 + Handler
類機制,即:
- 通過繼承
Thread
類,快速地建立1個帶有Looper
物件的新工作執行緒 - 通過封裝
Handler
類,快速建立Handler
& 與其他執行緒進行通訊
5. 使用步驟
HandlerThread
的本質:繼承Thread
類 & 封裝Handler
HandlerThread
的使用步驟分為5步
// 步驟1:建立HandlerThread例項物件
// 傳入引數 = 執行緒名字,作用 = 標記該執行緒
HandlerThread mHandlerThread = new HandlerThread("handlerThread");
// 步驟2:啟動執行緒
mHandlerThread.start();
// 步驟3:建立工作執行緒Handler & 複寫handleMessage()
// 作用:關聯HandlerThread的Looper物件、實現訊息處理操作 & 與其他執行緒進行通訊
// 注:訊息處理操作(HandlerMessage())的執行執行緒 = mHandlerThread所建立的工作執行緒中執行
Handler workHandler = new Handler( handlerThread.getLooper() ) {
@Override
public boolean handleMessage(Message msg) {
...//訊息處理
return true;
}
});
// 步驟4:使用工作執行緒Handler向工作執行緒的訊息佇列傳送訊息
// 在工作執行緒中,當訊息迴圈時取出對應訊息 & 在工作執行緒執行相關操作
// a. 定義要傳送的訊息
Message msg = Message.obtain();
msg.what = 2; //訊息的標識
msg.obj = "B"; // 訊息的存放
// b. 通過Handler傳送訊息到其繫結的訊息佇列
workHandler.sendMessage(msg);
// 步驟5:結束執行緒,即停止執行緒的訊息迴圈
mHandlerThread.quit();
6. 例項講解
下面,我將用一個例項講解HandlerThread
該如何使用
6.1 例項說明
- 點選按鈕實現延遲操作
- 最終更新UI元件
6.2 具體實現
- 主佈局檔案:activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:gravity="center"
tools:context="com.example.carson_ho.handler_learning.MainActivity">
<TextView
android:id="@+id/text1"
android:layout_centerInParent="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="測試結果" />
<Button
android:id="@+id/button1"
android:layout_centerInParent="true"
android:layout_below="@+id/text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="點選延遲1s + 顯示我愛學習"/>
<Button
android:id="@+id/button2"
android:layout_centerInParent="true"
android:layout_below="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="點選延遲3s + 顯示我不愛學習"/>
<Button
android:id="@+id/button3"
android:layout_centerInParent="true"
android:layout_below="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="結束執行緒的訊息迴圈"/>
</RelativeLayout>
- 主程式碼檔案:MainActivity.java
public class MainActivity extends AppCompatActivity {
Handler mainHandler,workHandler;
HandlerThread mHandlerThread;
TextView text;
Button button1,button2,button3;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 顯示文字
text = (TextView) findViewById(R.id.text1);
// 建立與主執行緒關聯的Handler
mainHandler = new Handler();
/**
* 步驟1:建立HandlerThread例項物件
* 傳入引數 = 執行緒名字,作用 = 標記該執行緒
*/
mHandlerThread = new HandlerThread("handlerThread");
/**
* 步驟2:啟動執行緒
*/
mHandlerThread.start();
/**
* 步驟3:建立工作執行緒Handler & 複寫handleMessage()
* 作用:關聯HandlerThread的Looper物件、實現訊息處理操作 & 與其他執行緒進行通訊
* 注:訊息處理操作(HandlerMessage())的執行執行緒 = mHandlerThread所建立的工作執行緒中執行
*/
workHandler = new Handler(mHandlerThread.getLooper()){
@Override
// 訊息處理的操作
public void handleMessage(Message msg)
{
//設定了兩種訊息處理操作,通過msg來進行識別
switch(msg.what){
// 訊息1
case 1:
try {
//延時操作
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 通過主執行緒Handler.post方法進行在主執行緒的UI更新操作
mainHandler.post(new Runnable() {
@Override
public void run () {
text.setText("我愛學習");
}
});
break;
// 訊息2
case 2:
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
mainHandler.post(new Runnable() {
@Override
public void run () {
text.setText("我不喜歡學習");
}
});
break;
default:
break;
}
}
};
/**
* 步驟4:使用工作執行緒Handler向工作執行緒的訊息佇列傳送訊息
* 在工作執行緒中,當訊息迴圈時取出對應訊息 & 在工作執行緒執行相關操作
*/
// 點選Button1
button1 = (Button) findViewById(R.id.button1);
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 通過sendMessage()傳送
// a. 定義要傳送的訊息
Message msg = Message.obtain();
msg.what = 1; //訊息的標識
msg.obj = "A"; // 訊息的存放
// b. 通過Handler傳送訊息到其繫結的訊息佇列
workHandler.sendMessage(msg);
}
});
// 點選Button2
button2 = (Button) findViewById(R.id.button2);
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 通過sendMessage()傳送
// a. 定義要傳送的訊息
Message msg = Message.obtain();
msg.what = 2; //訊息的標識
msg.obj = "B"; // 訊息的存放
// b. 通過Handler傳送訊息到其繫結的訊息佇列
workHandler.sendMessage(msg);
}
});
// 點選Button3
// 作用:退出訊息迴圈
button3 = (Button) findViewById(R.id.button3);
button3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mHandlerThread.quit();
}
});
}
}
- 執行結果
6.3 特別注意點
細節問題1:記憶體洩露
- 在上面的例子中,出現了嚴重的警告:
In Android, Handler classes should be static or leaks might occur.
- 1
細節問題2:連續傳送訊息
- 當你連續點選3下時,發現並無按照最新點選的按鈕操作顯示,而是按順序的一個個顯示出來
- 原因:使用
HandlerThread
時只是開了一個工作執行緒,當你點選了n
下後,只是將n
個訊息傳送到訊息佇列MessageQueue
裡排隊,等候派發訊息給Handler再進行對應的操作
7. 原始碼分析
- 知其然 而須知其所以然,瞭解
HandlerThread
的原始碼分析有利於更好地理解HandlerThread
的工作原理
8. 總結
- 本文全面介紹了多執行緒
HandlerThread
的用法 & 原始碼 - 接下來,我會繼續講解
Android
開發中關於多執行緒的知識,包括繼承Thread
類、實現Runnable
介面、Handler
等等,有興趣可以繼續關注Carson_Ho的安卓開發筆記