天天看點

每天學習一個Android中的常用架構——12.Handler

文章目錄

  • ​​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個部分進行一下簡要的介紹:

  1. ​Message​

    ​​ Message是線上程之間傳遞的消息,它可以在内部攜帶少量的資訊,用于在不同線程之間交換資料。我們可以使用到 Message的​

    ​what​

    ​字段來傳遞資訊,除此之外還可以使用​

    ​arg1​

    ​和​

    ​arg2​

    ​字段來攜帶一些整型資料,使用obj字段攜帶一個​

    ​Object​

    ​對象。
  2. ​Handler​

    ​​ Handler顧名思義也就是處理者的意思,它主要是用于發送和處理消息的。發送消息一般是使用Handler的​

    ​sendMessage()​

    ​方法,而發岀的消息經過一系列地輾轉處理後,最終會傳遞到Handler的 ​

    ​handleMessage()​

    ​方法中。
  3. ​MessageQueue​

    ​ MessageQueue是消息隊列的意思,它主要用于存放所有通過Handler發送的消息。這部分消息會一直存在于消息隊列中,等待被處理。每個線程中隻會有一個MessageQueue對象。
  4. ​Looper​

    ​​ Looper是每個線程中的MessageQueue的管家,調用Looper的​

    ​loop()​

    ​方法後,就會進入到一個無限循環當中,然後每當發現MessageQueue中存在一條消息,就會将它取出,并傳遞到Handler的​

    ​handleMessage()​

    ​方法中。每個線程中也隻會有一個Looper對象。

了解了​

​Message​

​​、​

​Handler​

​​、​

​MessageQueue​

​​和以及​

​Looper​

​的基本概念後,我們再來把正常的異步消息處理的整個流程梳理一遍。

  1. 首先需要在主線程當中建立一個Handler對象,并重寫​

    ​handleMessage()​

    ​方法。
  2. 然後當子線程中需要進行UI操作時,就建立一個Message對象,并通過Handler将這條消息發送出去。
  3. 之後這條消息會被添加到MessageQueue的隊列中等待被處理, 而Looper則會一直嘗試從MessageQueue中取岀待處理消息,最後分發回Handler的​

    ​handleMessage()​

    ​方法中。
  4. 由于Handler是在主線程中建立的,是以此時​

    ​handleMessage()​

    ​方法中的代碼也會在主線程中運作,于是我們在這裡就可以安心地進行UI操作了。整個異步消息處理機制的流程示意圖如圖所示:
  5. 每天學習一個Android中的常用架構——12.Handler

一條Message經過這樣一個流程的輾轉調用後,也就從子線程進入到了主線程,從不能更新UI變成了可以更新UI,整個異步消息處理的核心思想也就是如此。

而之前曾經使用到的​

​runOnUiThread()​

​方法其實就是一個異步消息處理機制的接口封裝,它雖然表面上看起來用法更為簡單,但其實背後的實作原理和這裡的描述是一樣的。

為了便于示範,這裡隻使用Handler中的一些常用的api來實作基本功能,若想要實作額外的功能可以參考CSDN上的其他博文,這裡不再贅述。

3.示範

3.1 內建

Handler預設就是內建在Android SDK中的,是以這一步可以省略,你可以在代碼中直接使用Handler來處理消息機制。

3.2 建立Message

在使用Handler來處理消息機制時,我們應該先擷取到Message的對象執行個體。Message的建立總共有三種方式:

  1. 通過Message類的構造方法,即:​

    ​Message message = new Message();​

  2. 用過Message類的​

    ​obtain()​

    ​方法,即:​

    ​Message message = Message.obtain();​

    ​推薦使用這種方式來擷取Message的對象執行個體,因為Message在回收時會被放入到一個對象池中
  3. 通過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類裡的方法,其中主要的方法如下:

  1. 直接發送消息:​

    ​mHandler.sendMessage(message);​

  2. 延時1s後發送消息:​

    ​mHandler.sendMessageDelayed(message,1000);​

  3. 發送帶标記的消息(需要内部建立message,并設定​

    ​msg.what = 0x1​

    ​​):​

    ​mHandler.sendEmptyMessage(0x1);​

  4. 延時1s後發送帶标記的消息(需要内部建立message,并設定​

    ​msg.what = 0x1​

    ​​):​

    ​mHandler.sendEmptyMessageDelayed(0x1,1);​

  5. 移除标記為​

    ​0x1​

    ​​的消息:​

    ​mHandler.removeMessages(0x1);​

  6. 移除回調的消息:​

    ​mHandler.removeCallbacks(Runnable);​

  7. 移除回調和所有的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 總結

  1. 建立Message對象,設定其​

    ​what​

    ​字段;
  2. 建立Handler對象,重寫其​

    ​handlerMessage()​

    ​方法;
  3. 發送Message對象,使用Handler的​

    ​sendMessage()​

    ​​方法,然後在​

    ​handlerMessage()​

    ​中進行相應的事件處理。

4.源碼位址