Android Looper:如何實現消息循環和異步執行任務

一、了解Android消息機制

Android消息機制是指通過 Message、Handler 和 Looper 三者相互配合,實現在子線程中發送消息、處理消息,從而實現異步執行任務的方案。其中,Message 負責攜帶消息的內容,Handler 負責處理消息,而 Looper 則是消息循環機制的核心。在此機制下,我們可以在主線程中往子線程中發送消息,也可以在子線程中向主線程發送消息,從而實現更加靈活的多線程編程。

二、學習Looper類的基本結構

public class Looper {
    private static final Object sLock = new Object();
    private static Looper sMainLooper = null;
    final MessageQueue mQueue;
    private final Thread mThread;
    private boolean mRun;

    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

    public static void prepare() {
        prepare(true);
    }

    public static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

    public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;

        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                return;
            }
            try {
                msg.target.dispatchMessage(msg);
            } finally {
                msg.recycleUnchecked();
            }
        }
    }

    public static void quit() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        synchronized (me.mQueue) {
            me.mRun = false;
            me.mQueue.notifyAll();
        }
    }

    public static Looper myLooper() {
        return sThreadLocal.get();
    }
}

以上是 Looper 類的源碼,其中最重要的屬性是 MessageQueue,它通過鏈表的結構保存所有需要執行的消息。Looper 自身則主要是調用 MessageQueue 的 next 方法進行輪詢,然後將消息交給對應的 Handler 處理,最後將 Message 回收利用以節約資源。

三、了解Message的實現

public final class Message {
    public int what;
    public int arg1;
    public int arg2;
    public Object obj;
    public long when;
    public Handler target;
    Message next;
    public int sendingUid = -1;
    public Bundle data;

    private static final Object sPoolSync = new Object();
    private static Message sPool;

    private static int sPoolSize = 0;
    private static final int MAX_POOL_SIZE = 50;
    private static boolean gCheckRecycle = true;
    private boolean mRecycled;

    public static final int FLAG_IN_USE = 1 << 0;
    int flags;
    private static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE;

    private static boolean gCheckRecycleUnchecked = true;

    private Message() {}

    public static Message obtain(Handler h) {
        Message m = get();
        m.target = h;

        return m;
    }

    private static Message get() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0;
                m.sendingUid = -1;
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

    void recycleUnchecked() {
        if (mRecycled) {
            if (gCheckRecycleUnchecked) {
                throw new IllegalStateException("This message cannot be recycled because it "
                        + "is still in use.");
            }
            return;
        }
        mRecycled = true;
        clearForRecycle();
        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

    void clearForRecycle() {
        flags = 0;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        when = 0;
        target = null;
        // Explicitly target data to its more specific implementation to ensure
        // that it's correctly recycled after a transfer.
        data = null;
    }
}

以上代碼是 Message 類的實現代碼,它主要包括消息類型和消息內容,配合 Handler 來執行對應的操作。其中 mostPoolSize 規定了該類允許緩存的最大實例數量,可以看到它通過靜態 sPoolSync 鎖實現了資源的共享,以此達到了 Message 回收、利用和避免浪費的效果。

四、創建一個自定義Handler進行消息處理

public class MyHandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        // 根據不同的消息類型,執行對應的操作
        switch (msg.what) {
            case 1:
                // 執行操作1
                break;
            case 2:
                // 執行操作2
                break;
            default:
                break;
        }
    }
}

通過繼承 Handler 類,我們可以在自定義的 handleMessage 方法中處理各種不同消息類型的執行操作。其中,msg.what 用於表示消息類型,可以根據不同的需求來自定義,比如用數字表示不同的操作,或者用 final 常量來表示特定的操作。

五、在子線程中使用 Looper

public class MyThread extends Thread {
    @Override
    public void run() {
        Looper.prepare();
        MyHandler handler = new MyHandler();
        Looper.loop();
    }

    public void sendMessage(Message msg) {
        if (handler != null) {
            handler.sendMessage(msg);
        }
    }
}

以上代碼演示了如何在子線程中使用 Looper。通過調用 Looper 類的 prepare 方法和 loop 方法,我們可以開始一個消息循環。在此循環中,我們可以根據不同的需求,使用自定義的 Handler 來處理消息邏輯。而 sendMessage 方法則是用於在 MyThread 中發送消息所用。

六、在主線程中使用 Handler

public class MainActivity extends AppCompatActivity {
    private MyThread myThread;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        myThread = new MyThread();
        myThread.start();

        findViewById(R.id.btn_send_message).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Message msg = Message.obtain();
                msg.what = 1;
                myThread.sendMessage(msg);
            }
        });
    }
}

可以看到,MainActivity 中的 onCreate 方法中創建了一個 MyThread 線程,並在點擊按鈕的回調中發送了一個攜帶了消息類型為1的消息,這樣就能夠在子線程中執行對應的邏輯操作了。

七、總結

本文以 Android 中消息機制的核心類 Looper、Message 和 Handler 為中心,詳細介紹了消息循環、消息獲取、消息分發過程,還有如何在子線程中創建 Looper、Handler,以及如何在主線程中發送消息觸發相應的處理操作。只有充分理解 Android 的消息機制,才能更好的進行多線程編程,提高應用程序的性能與良好交互體驗。

原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-hant/n/285441.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
小藍的頭像小藍
上一篇 2024-12-22 15:44
下一篇 2024-12-22 15:44

相關推薦

  • Java任務下發回滾系統的設計與實現

    本文將介紹一個Java任務下發回滾系統的設計與實現。該系統可以用於執行複雜的任務,包括可回滾的任務,及時恢復任務失敗前的狀態。系統使用Java語言進行開發,可以支持多種類型的任務。…

    編程 2025-04-29
  • 如何實現圖像粘貼到蒙版

    本文將從多個方面介紹圖像粘貼到蒙版的實現方法。 一、創建蒙版 首先,在HTML中創建一個蒙版元素,用於接收要粘貼的圖片。 <div id=”mask” style=”widt…

    編程 2025-04-29
  • RabbitMQ和Yii2的消息隊列應用

    本文將探討RabbitMQ和Yii2之間的消息隊列應用。從概念、安裝和配置、使用實例等多個方面詳細講解,幫助讀者了解和掌握RabbitMQ和Yii2的消息隊列應用。 一、Rabbi…

    編程 2025-04-29
  • Saturn 定時任務用法介紹

    本文將從以下幾個方面對Saturn定時任務進行詳細的闡述: 一、Saturn 定時任務簡介 Saturn是一個分布式任務調度系統,支持在線添加、修改定時任務,支持多種任務類型,如J…

    編程 2025-04-29
  • Django ORM如何實現或的條件查詢

    在我們使用Django進行數據庫操作的時候,查詢條件往往不止一個,一個好的查詢語句需要考慮我們的查詢要求以及業務場景。在實際工作中,我們經常需要使用或的條件進行查詢,本文將詳細介紹…

    編程 2025-04-29
  • Python一次性輸入10個數如何實現?

    Python提供了多種方法進行輸入,可以手動逐個輸入,也可以一次性輸入多個數。在需要輸入大量數據時,一次性輸入十個數就非常方便。下面我們從多個方面來講解如何一次性輸入10個數。 一…

    編程 2025-04-28
  • ROS線程發布消息異常解決方法

    針對ROS線程發布消息異常問題,我們可以從以下幾個方面進行分析和解決。 一、檢查ROS代碼是否正確 首先,我們需要檢查ROS代碼是否正確。可能會出現的問題包括: 是否正確初始化RO…

    編程 2025-04-28
  • 使用Python發送微信消息給別人

    問題:如何使用Python發送微信消息給別人? 一、配置微信開發者平台 首先,要想發送微信消息,需要在微信開發者平台中進行配置,來獲取對應的授權信息。具體步驟如下: 1、登錄微信公…

    編程 2025-04-28
  • 如何在dolphinscheduler中運行chunjun任務實例

    本文將從多個方面對dolphinscheduler運行chunjun任務實例進行詳細的闡述,包括準備工作、chunjun任務配置、運行結果等方面。 一、準備工作 在運行chunju…

    編程 2025-04-28
  • Android ViewPager和ScrollView滑動衝突問題

    Android開發中,ViewPager和ScrollView是兩個常用的控件。但是當它們同時使用時,可能會發生滑動衝突的問題。本文將從多個方面介紹解決Android ViewPa…

    編程 2025-04-28

發表回復

登錄後才能評論