Android中的消息機制


個人博客:http://zhangsunyucong.top

前言

Android中的消息機制是指線程之間的通信機制。我們都知道,如果我們在UI主線程中做耗時的操作而無法及時處理時,程序會彈出ANR全名Application Not Responding, 也就是”應用無響應”的對話框。

首先來一張圖,從整體上來看一下android消息機制。

view繼承關係

Handler:用於發送消息和處理消息
MessageQueue: 一個先進先出的消息隊列
Looper:循環者,它不斷的循環的遍歷查詢消息隊列

Looper中會創建一個消息隊列,並進入消息循環,不斷的從消息隊列中取出消息,然後分發消息給對應的消息處理函數,如果消息隊列爲空,它會進入阻塞等待,直到有新的消息到來,然後被喚醒。

源碼分析

Looper

     private 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));
    }

這就是Looper的創建函數,它創建了一個Looper實例並放到ThreadLocal中。
ThreadLocal是一個線程共享和線程安全的,ThreadLocal變量在不同的線程中有不同的副本。

這裏,首先檢查線程是否有Looper,如果已經有,就報”Only one Looper may be created per thread”異常。也就是說一個線程只能有一個Looper,不能重複創建。

進入Looper的構造函數

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

Looper的構造函數中主要是創建了一個消息隊列,和賦值當前線程變量。

開啓消息循環

    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;

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();
        //進入循環,不斷的從消息隊列中取出消息,如果沒有消息會進入阻塞狀態
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;

            final long traceTag = me.mTraceTag;
            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            final long end;
            try {
                //將消息分發給對應的handler處理
                msg.target.dispatchMessage(msg);
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            if (slowDispatchThresholdMs > 0) {
                final long time = end - start;
                if (time > slowDispatchThresholdMs) {
                    Slog.w(TAG, "Dispatch took " + time + "ms on "
                            + Thread.currentThread().getName() + ", h=" +
                            msg.target + " cb=" + msg.callback + " msg=" + msg.what);
                }
            }

            if (logging != null) {
                logging.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(TAG, "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.recycleUnchecked();
        }
    }

Looper#loop方法的工作,在代碼中已經進行註釋說明。

Looper#loop中會將消息分發給對應的handler處理。

msg.target.dispatchMessage(msg);

現在我們進入handler。

Handler

Handler handler = new Handler(Looper.myLooper());

首先看Handler的構造函數,可以知道Handler是怎麼和Looper取得關聯的。

public Handler(Looper looper, Callback callback, boolean async) 
{
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

主要爲Handler的四個變量賦值,其中確定了Handler是和哪一個Looper關聯,和Handler發送消息到對應的哪個消息隊列。可以知道,一個Handler只有一個Looper和對應的MessageQueue。而不同的Handler可以共享同一個Looper和MessageQueue,這就看你在初始化Handler時與哪個Looper關聯了。

Handler無參數的構造函數是和哪個Looper關聯呢?

    public Handler() {
        this(null, false);
    }

    public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

        //取出當前線程的循環者
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

Handler無參數的構造函數仍然主要是爲那四個變量賦值。它會首先取出當前線程的消息循環者,如果線程沒有循環者,會報一個異常。

發送消息到循環隊列

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

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

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
    if (queue == null) {
        RuntimeException e = new RuntimeException(
            this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
        return false;
    }
    return enqueueMessage(queue, msg, uptimeMillis);
}

sendMessage中會調用sendMessageDelayed,sendMessageDelayed再調用sendMessageAtTime,最後會調用enqueueMessage將消息入隊。post開頭的方法是調用相應send開頭的方法的。

進入Handler#enqueueMessage

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

在分析Looper#loop時,其中有將消息的分發給相應的Handler處理的邏輯,而正是在第2行代碼時,它們取得聯繫的。然後將消息放入Handler關聯的Looper中的消息隊列。

在MessageQueue#enqueueMessage中,消息入隊時,如果消息隊列是阻塞休眠狀態,會喚醒消息隊列。

if (p == null || when == 0 || when < p.when) {
    // New head, wake up the event queue if blocked.
    msg.next = p;
    mMessages = msg;
    needWake = mBlocked;
}

在Looper#loop中,會將消息分發給對應的Handler處理函數dispatchMessage處理

msg.target.dispatchMessage(msg);

進入Handler#dispatchMessage

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

java.lang.Callback

public interface Runnable {
    public abstract void run();
}

Handler#Callback

public interface Callback {
    public boolean handleMessage(Message msg);
}

優先調用Message的callback接口,如果Handler有Callback,調用Callback,否則會調用handleMessage方法。

Handler#handleMessage

public void handleMessage(Message msg) {
}

這是一個空方法,具體的消息邏輯由我們自己定義。

到此,這個流程已經解釋完畢

後話

在非UI線程中只要找好時機也是可以更新UI的。這個會在源碼再分析。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章