Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 面試時說的Android消息機制

面試時說的Android消息機制

編輯:關於Android編程

Android的消息機制主要指Handler的運行機制,而Handler的運行又需要底層的MessageQueue和Looper的支撐。   總體流程圖

MessageQueue

消息隊列,但是內部存儲結構並不是真正的隊列,而是采用單鏈表的數據結構來存儲消息列表。

Looper

由於MessageQueue只是一個消息的存儲單元,它不能去處理消息,而Looper就填補了這個功能,Looper會以無限循環的形式去查找是否有新消息,如果有的話就處理消息,否則就一直等待著。

Handler

主要作用是將一個任務切換到某個指定的線程中去執行。

系統之所以提供Handler,主要原因就是為了解決在子線程中無法訪問UI的矛盾。

系統為什麼不允許在子線程中訪問UI呢?

因為Android的UI控件不是線程安全的,如果在多線程中並發訪問可能會導致UI控 件處於不可預期的狀態。

那竟然不安全,為什麼系統不對UI控件的訪問加上鎖機制呢?

加鎖後會有2個缺點:首先加上鎖機制會讓UI訪問的邏輯變得復雜;其次鎖機制會降低UI訪問的效率,因為鎖機制會阻塞某些線程的執行。

第一步:Handler發消息msg(在子線程中發送message,在主線程中創建Handler)

創建Handler完畢後,

  1. 通過Handler的post方法將一個Runnable投遞到Handler內部的Looper中去處理,

  2. 也可以通過Handler的send方法發送一個消息,這個消息同樣會在Looper中去處理。

    其實post方法最終也是通過send方法來完成。

步驟流程: sendMessage(msg)-->sendMessageDelayed(Message msg, long delayMillis)-->sendMessageAtTime(Message msg, long uptimeMillis)-->enqueueMessage(Message msg, long when)

這裡的enqueueMessage方法是調用了MessageQueue的enqueueMessage方法

//1.發消息
handler.sendMessage(msg);//發消息

public final boolean sendMessage(Message msg)
{
    //2 sendMessageDelayed
    return sendMessageDelayed(msg, 0);
}

//2 sendMessageDelayed

public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    //3 sendMessageAtTime
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

//3 sendMessageAtTime
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
{
    boolean sent = false;
    //this 就是 handler mQueue 就是從Looper中取出來的消息隊列
    MessageQueue queue = mQueue;
    if (queue != null) {
        //handler發消息 把當前的handler對象綁定到Message對象中
        msg.target = this;
        // 把消息放到消息隊列裡,排序 ,如果你調用sendMessage(msg),uptimeMillis = 0
        //4 enqueueMessage
        sent = queue.enqueueMessage(msg, uptimeMillis);
    }
    。。。
    return sent;
}

 //4 enqueueMessage

final boolean enqueueMessage(Message msg, long when) {


    final boolean needWake;
    synchronized (this) {
        if (mQuiting) {
            RuntimeException e = new RuntimeException(
                msg.target + " sending message to a Handler on a dead thread");
            Log.w("MessageQueue", e.getMessage(), e);
            return false;
        } else if (msg.target == null) {
            mQuiting = true;
        }

        msg.when = when;
        //Log.d("MessageQueue", "Enqueing: " + msg);
        //mMessages 是MessageQueue對象裡的屬性
        Message p = mMessages;
        if (p == null || when == 0 || when < p.when) {
           //msg 發的消息
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked; // new head, might need to wake up
        } else {
            Message prev = null;
            while (p != null && p.when <= when) {
                prev = p;
                p = p.next;
            }
            msg.next = prev.next;
            prev.next = msg;
            needWake = false; // still waiting on head, no need to wake up
        }
    }
    if (needWake) {
        nativeWake(mPtr);//jni
    }
    return true;
}
第二步:當Handler的send方法被調用時,它最後會調用MessageQueue的enqueueMessage方法將這個消息放入消息隊列中

消息隊列在Android中指的是MessageQueue, MessageQueue主要包含兩個操作插入和讀取

插入 對應的方法為enqueueMessage

  • enqueueMessage的作用是往消息隊列中插入一條消息

    讀取 對應的方法為next

    • next的作用是從消息隊列中取出一條消息並將其從消息隊列中移除

從enqueueMessage的實現來看,它的主要操作其實就是單鏈表的插入操作。

打插入消息的動作並不復雜,無非是在消息鏈表中找到合適的位置,插入Message節點而已。因為消息鏈表是按時間進行排序的,所以主要是在比對Message攜帶的when信息。消息鏈表的首個節點對應著最先將被處理的消息,如果Message被插到鏈表的頭部了,就意味著隊列的最近喚醒時間也應該被調整了,因此needWake會被設為true,以便代碼下方可以走進nativeWake()。

從next的實現來看,它是一個無限循環的方法,如果消息隊列中沒有信息,那麼next方法會一直阻塞在這裡。當有新消息到來時,next方法會返回這條消息並將其從單鏈表中移除。

第三步:當Handler發送過來的消息message打入到消息隊列後,Looper發現有新消息到來時,就會處理這個消息,最終消息中的Runnable或者Handler的handleMessage方法就會被調用。

Looper(輪詢器),會不停地從MessageQueue中查看是否有新消息,如果有新消息就會立刻處理,否則就一直阻塞在那裡。

我們知道,Handler的工作需要Looper,沒有Looper的線程就會報錯,那麼如何為一個線程創建Looper呢?其實很簡單,通過Looper.prepare()即可以為當前線程創建一個Looper,接著通過Looper.loop()來開啟消息循環     當一個線程運行到某處,准備運作一個Looper時,它必須先調用Looper類的靜態函數prepare(),做一些准備工作。說穿了就是創建一個Looper對象,並把它設置進線程的本地存儲區(TLS)裡。然後線程才能繼續調用Looper類的另一個靜態函數loop(),從而建立起消息處理循環。示意圖如下:
* ActivityThread類中初始化,應用程序運行之前執行,main方法如下:

 //主線程
 public static final void main(String[] args) {
    。。。
    //輪詢器的初始化
    Looper.prepareMainLooper();

    。。。
    //輪詢器開始取消息
    Looper.loop();


}

* loop方法

  public static final void loop() {
    Looper me = myLooper();
    MessageQueue queue = me.mQueue;

    while (true) {
        //沒有消息阻塞
        Message msg = queue.next(); // might block

        if (msg != null) {
            if (msg.target == null) {
                // No target is a magic identifier for the quit message.
                return;
            }
            if (me.mLogging!= null) me.mLogging.println(
                    ">>>>> Dispatching to " + msg.target + " "
                    + msg.callback + ": " + msg.what
                    );
            msg.target.dispatchMessage(msg);
            if (me.mLogging!= null) me.mLogging.println(
                    "<<<<< Finished to    " + msg.target + " "
                    + msg.callback);

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf("Looper", "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }

            msg.recycle();
        }
    }
}

Looper往消息隊列中取消息,其中loop方法是一個死循環,唯一跳出循環的方式是MessageQueue的next方法返回了null

  public static final void loop() {
    Looper me = myLooper();
    MessageQueue queue = me.mQueue;

    while (true) {
        //沒有消息阻塞
        //從消息隊列裡取消息頭
        Message msg = queue.next(); // might block

        if (msg != null) {

            ...
             打印日志
            。。。
           處理消息
            msg.target.dispatchMessage(msg);
            。。
            回收消息
            msg.recycle();
        }
    }
}

  處理消息
  msg.target.dispatchMessage(msg);

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {//runOu......
        //處理runOnUithread方法
       //1
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        //調用handleMessage 主線程
        handleMessage(msg);
    }
}
//1.
  handleCallback(msg);

//2.
private final void handleCallback(Message message) {
    message.callback.run();//runnable 跟線程沒關系
}
\   處理消息過程: 如果MessageQueue的next方法返回了新消息,Looper就會處理這條消息:msg.target.dispatchMessage(msg);,這裡的msg.target是發送這條消息的Handler對象,這樣Handler發送的消息最終又交給它的dispatchMessage方法處理了。但是這裡不同的是,Handler的dispatchMessage方法是在創建Handler時所使用的Looper中執行的,這樣就成功地將代碼邏輯切換到指定的線程中去執行了,最後還是由對應的線程Handler調用handlerMessage方法。     從源碼中我們可以發現,Handler發送消息的過程僅僅是向消息隊列中插入了一條消息,MessageQueue的next方法就會返回這條消息給Looper,Looper收到消息後就開始處理了,最終消息由Looper交由Handler處理。即Handler的dispatchMessage方法會被調用,這時Handler就進入了處理消息的階段。
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved