Android消息機制

一、UI線程

什麼是UI線程?

Android應用在啓動時間會首先創建一個主線程(main thread),它是應用程序的入口,主要負責管理UI以及分發事件,習慣上稱之爲UI線程(UI thread)。

1、  不是線程安全的,對UI的操作操作必須在UI線程中進行,否則系統將拋出異常;

ProgressBar通過setProgress更新UI

在更新進度的時候會判斷當前線程是否爲UI線程,是UI線程直接調用刷新方法,不是的話就調用view的post方法,將runable放到UI線程的消息隊列等待處理,這個就涉及到Handler,下文再講。

Android提供了以下一些方法,從其他線程訪問UI線程:

·        Activity.runOnUiThread(Runnable)

·        View.post(Runnable)

·        View.postDelayed(Runnable,long)

2、UI線程不能被阻塞,阻塞時間超過5s,出現ANR,影響用戶體驗,對於耗時的操作一定要在非UI線程中執行。 

二、消息系統與消息模型

Android實現自己的消息系統,抽象出Message、MessageQueue、Looper、Handler等概念,這些組件巧妙結合形成了Android的消息模型,首先我們瞭解一下消息系統的構成要素和基本原理。

2.1 消息系統構成要素和基本原理

從一般的系統設計層次來說,基本的消息循環系統需要包含以下幾個要素。

●消息隊列

●發送消息

●消息讀取

●消息分發

●消息循環線程

    消息系統必須要依賴一個消息循環來輪詢自己的消息隊列,如果有消息進來,就調用消息處理函數,根據消息類型及其參數做相應的處理。消息系統要運作起來,必定有消息的產生和消費。暫且把產生消息的線程稱作生產者線程,把消費者線程稱作消費者線程。生產者線程將消息發送到消息隊列,消費者線程從消息隊列中取出消息進行相應的處理。當消息隊列中沒有消息時,消費者線程便進入了掛起狀態,而當有新的消息到達時,消費者線程會被喚醒繼續運行。當然生產者也可以是消費者。

 

 

 

圖2.1 基本的消息循環模型


2.2 消息模型基本原理

     Android的消息系統使用了Message、MessageQueue、Looper、Handler等概念,從消息系統的基本原理可以知道這些都是概念包裝,本質的東西是消息隊列中消息的分發處理方法。Android巧妙地利用了對象抽象技術抽象出了Looper和Handler的概念,並在Looper和Handler概念的基礎上,通過View的處理框架,十分完美地實現了消息分發的功能。下面對這幾個概念進行詳細介紹。

2.2.1 Message

消息對象,它是信息的載體,線程間通訊的數據單元。例如後臺線程在處理數據完畢後需要更新UI,則可發送一條包含更新信息的Message給UI線程。

Message通常存放在消息隊列(MessageQueue)中,一個MessageQueue可以包含多個Message。

創建實例,obtain(),該方法有多個重載,不一一介紹。

     public static Message obtain() {
         synchronized (sPoolSync) {
             if (sPool != null) {
                 Message m = sPool;
                 sPool = m.next;
                 m.next = null;
                 sPoolSize--;
                 return m;
             }
        }
        return new Message();
    }

該方法獲取Message時並不是直接創建一個新的實例,而是先從Message Pool(消息池)中查看有沒有可用的Message實例,如果有,則直接複用這個Message實例;如果沒有,創建一個新的Messages實例。

消息回收,recycle()

    public void recycle() {
        clearForRecycle();
 
        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

系統處理完消息之後,並不是直接將消息消除,而是放到消息池當中(最大值爲50個,若消息池中已經有50個Message,則丟棄不保存)。

這麼做的好處就是消息不被使用時,並不作爲垃圾回收,而是放入消息池,可供下次創建消息時使用。消息池提高了消息對象的複用,減少系統垃圾回收的次數。

Message相關內容就說這麼多。 

2.2.2 MessageQueue

是一種數據結構,具有先進先出的特點,用來存放消息隊列。每個線程最多擁有一個MessagQueue。創建線程時,並不會自動創建對應的MessageQueue。通常使用Looper對象對線程的MessageQueue進行管理。Android應用程序在創建主線程時,會默認創建一個Looper對象,該對象創建時,會自動創建一個MessageQueue。其他線程不會自動創建Looper,需要的時候可以通過調用Looper的prepare()函數創建。

MessageQueue封裝在Looper中,用戶一般很少去接觸,不再詳細介紹。

2.2.3 Looper

MessageQueue的管理者,每一個MessageQueue都不能脫離Looper而單獨存在。

Looper對象的創建是通過prepare函數來實現的。同時每一個Looper對象和一個線程關聯。通過調用Looper.myLooper()可以獲得當前線程的Looper對象 。創建一個Looper對

    public void recycle() {
        clearForRecycle();
 
        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

Looper對象創建好後,Looper線程要真正工作,需要調用loop()方法,它不斷從自己的MQ中取出隊頭的消息,將Message交給Message的target進行處理,處理完之後,調用Message.recycle()放入消息池中,消息隊列中沒有消息時會退出循環。

    public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("NoLooper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;
 
        // Make sure the identity of thisthread is that of the local process,
        // and keep track of what that identitytoken actually is.
        Binder.clearCallingIdentity();
        final long ident =Binder.clearCallingIdentity();
 
        for (;;) {
            Message msg = queue.next(); //might block
            if (msg == null) {
                // No message indicates thatthe message queue is quitting.
                return;
            }
 
.................................
            msg.target.dispatchMessage(msg);
 
.................................
            msg.recycle();
        }
    }

除了主線程有默認的Looper,其他線程默認是沒有Looper對象的,所以,不能接受Message。如需要接受,自己定義一個Looper對象(通過prepare函數),這樣該線程就有了自己的Looper對象和MessageQueue數據結構了。 具有Looper對象的線程我們也稱之爲“Looper線程”。

如何創建一個Looper線程?

public class LooperThread extends Thread {
    @Override
    publicvoid run() {
       // 將當前線程初始化爲Looper線程
       Looper.prepare();
      
       // ...其他處理,如實例化handler
      
       // 開始循環處理消息隊列
       Looper.loop();
    }
}

但是Android是一個比較成熟的系統,所以我們一般不直接去創建Looper線程,android提供了HandlerThread,這是一個帶有消息循環的線程,它有自己的消息隊列,能夠接收其他線程發送的消息。

除了prepare()和loop()方法,Looper類還提供了一些有用的方法,

    public static final Looper myLooper() {
        // 在任意線程調用Looper.myLooper()返回的都是那個線程的looper
        return(Looper)sThreadLocal.get();
    }
 
    public Thread getThread() {//得到looper對象所屬線程
        return mThread;
    }
 
    public void quit() {//結束looper循環
        // 創建一個空的message,它的target爲NULL,表示結束循環消息
        Message msg = Message.obtain();
        // 發出消息
        mQueue.enqueueMessage(msg, 0);
    }

通過以上的一些介紹,對Looper可以總結爲以下幾點:

●每個線程有且最多只能有一個Looper對象;

●Looper內部有一個消息隊列,loop()方法調用後線程開始不斷從隊列中取出消息執行;

●Looper使一個線程變成Looper線程。

那麼,如何將將消息添加到消息隊列以及處理消息呢?且看下文。

2.2.4 Handler

消息的發送者和處理者,扮演了往MQ上添加消息和處理消息的角色(只處理由自己發出的消息)。

Handler對象通過obtainMessage()方法,將需要傳遞的信息封裝成Message對象,調用sendMessage()方法將消息傳遞給Looper,然後由Looper將Message放入MessageQueue中,具體操作在Looper中已經介紹,不再贅述。最後通過Message對應的Handler的handleMessage()進行處理。

Handler只能在它所在的線程上下文中取得消息隊列,然後對消息隊列操作,如果外部線程需要向某個線程發送消息,必須先獲取某個線程中的任意Handler對象,然後通過Handler對象進行發送消息或者刪除消息。

Handler創建時會關聯一個Looper,默認的構造方法將關聯當前線程的looper,不過這也是可以設定的。

    public Handler(Callback callback, booleanasync) {
....................
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handlerinside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
 
    public Handler(Looper looper, Callbackcallback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

Handler發送消息

Handler使用 post(Runnable)postAtTime(Runnable,long)

postDelayed(Runnable,long)sendEmptyMessage(int),sendMessage(Message)sendMessageAtTime(Message,long)和 sendMessageDelayed(Message,long)這些方法向MQ上發送消息,光看這些API你可能會覺得handler能發兩種消息,一種是Runnable對象,一種是Message對象,這是直觀的理解,但其實post發出的Runnable對象最後都被封裝成message對象了。以post(Runnable r)爲例

    public final boolean post(Runnable r)
    {
       return sendMessageDelayed(getPostMessage(r), 0);
    }
    private static MessagegetPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

Handler發送的消息有如下特點:

●message.target爲該handler對象,這確保了looper執行到該message時能找到處理它的handler,即loop()方法中的關鍵代碼;

●post發出的Message,其callback爲Runnable對象。

Handler處理消息

    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if(mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

dispatchMessage()方法裏,如何處理Message則由用戶指定,三個判斷,優先級從高到低:

●Message裏面的Callback,一個實現了Runnable接口的對象,其中run函數做處理工作;

●Handler裏面mCallback指向的一個實現了Callback接口的對象,由其handleMessage進行處理;

●處理消息Handler對象對應的類繼承並實現了其中handleMessage函數,通過這個實現的handleMessage函數處理消息。

通過對以上幾個概念的介紹,我們對消息的處理流程有了一定理解,其流程基本可以概括爲一下幾點:

●包裝Message對象(指定Handler、回調函數和攜帶數據等);

●通過Handler的sendMessage()等類似方法將Message發送出去;

●在Handler的處理方法裏面將Message添加到Handler綁定的Looper的MessageQueue;

●Looper的loop()方法通過循環不斷從MessageQueue裏面提取Message進行處理,並移除處理完畢的Message;

●通過調用Message綁定的Handler對象的dispatchMessage()方法完成對消息的處理。

我們可以用下圖來表示android的消息模型


圖2.2 Android消息模型

 

 

發佈了35 篇原創文章 · 獲贊 10 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章