1. 程式人生 > >Android多執行緒:HandlerThread詳細使用手冊

Android多執行緒:HandlerThread詳細使用手冊

  • 多執行緒的應用在Android開發中是非常常見的,常用方法主要有:

  1. 繼承Thread類
  2. 實現Runnable介面
  3. Handler
  4. AsyncTask
  5. HandlerThread
  • 今天,我將全面解析多執行緒其中一種常見用法:HandlerThread

  • 目錄

    示意圖

    1. 定義

    一個Android 已封裝好的輕量級非同步類

    2. 作用

    1. 實現多執行緒 在工作執行緒中執行任務,如 耗時任務
    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 例項說明

    1. 點選按鈕實現延遲操作
    2. 最終更新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的安卓開發筆記

    請幫頂 / 評論點贊!因為你的鼓勵是我寫作的最大動力!