Android10_原理機制系列_Android消息機制(Handler)詳述

概述

Android中的多進程、多線程中提過,只有主線程(UI線程)能夠更新UI,其餘線程不能夠,因此通常耗時操做放到子線程。子線程能夠經過Handler將相關信息通知到主線程。
Android的消息機制主要是Handler機制。Handler的工做過程,還有兩個重要部分MessageQueue(消息隊列,下面簡稱MQ)和Looper。html

因爲下面總結中穿插了很多源碼 便於理解,致使篇幅比較長(加代碼有600多行)。因此先大體總結並給出大體目錄,提早了解是不是須要的內容。java

大體總結

消息機制的大體流程

  1. 線程建立Looper(調用Looper.preper()),而後運行Looper.looper()開啓循環。Looper是和線程綁定的,一個線程只能有一個Looper ,一個Looper內部維護一個MQ。
  2. Handler調用sendMessage()方法發送消息,將消息加入到MQ中(enqueueMessage())。
  3. Looper死循環,經過MessageQueue.next()取出符合的消息。
  4. 取出消息後,經過msg.target.dispatchMessage()分發消息。
  5. Handler對接受到的消息進行具體處理,調用dispatchMessage()。
  6. 調用Looper的quit()方法終止,即消息隊列退出、looper循環退出。

注意點

  1. 建立Handler的線程中必定先有Looper對象, 才能建立Handler,不然會拋異常。下面詳解中經過代碼就能看出來。
  2. 一個線程只能有一個Looper。但能夠建立多個Handler
  3. MQ是Looper內存維護的。
  4. 主線程在應用啓動後默認建立Looper 且不能退出。

跨線程大體理解

Handler能作到跨線程,主要是Looper及內部的消息隊列。最多見的:程序啓動主線程建立Looper並綁定了消息隊列,在主線程建立Handler,這個Handler與Looper綁定的。在其餘線程(任何地方)經過這個Handler發送消息,消息都加入到了主線程Looper內部的消息隊列(消息發送到的MQ是 建立Handler時綁定的Looper內部MQ),當消息被Looper循環取出,天然就回到了主線程android

大體目錄

1       Looper、Handler與MQ     
1.1     Looper     
1.1.1   Looper的建立:prepare()     
1.1.2   Looper循環:loop()    

1.2     Handler     
1.2.1   Handler的建立    
1.2.2   Handler發送消息    
1.2.3   Handler分派處理:dispatchMessage    

1.3     MessageQueue    
1.3.1   入隊:enqueueMessage()    
1.3.2   next()方法    
1.3.3   退出:quit()    

2       其餘注意點      
2.1     Handler通常使用         
2.2     消息池          
2.3     子線程到主線程的方法            
2.4     主線程的Looper         
2.5     ANR問題

Looper、Handler與MQ

Looper

Looper是循環器,爲一個線程運行消息循環,不斷檢查消息隊列中是否有新的消息。
Looper.prepare()爲當前線程建立一個looper,並在其內部維護一個MQ。
Looper.loop()即looper開始工做,運行消息循環。緩存

下面是Looper部分的幾處源碼,有助於理解。安全

Looper的建立:prepare()

// sThreadLocal.get() will return null unless you've called prepare().
@UnsupportedAppUsage
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

@UnsupportedAppUsage
final MessageQueue mQueue;
final Thread mThread;

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

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

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

Looper經過prepare()方法建立,主要有如下幾點:數據結構

  1. 默認建立Looper時,設置參數的爲true,即消息隊列是可以退出的。MQ的退出也提供了安全退出和非安全退出兩種方法(在下面MQ部分中詳述)。
  2. 一個線程只能建立一個Looper,不然會拋出RuntimeException。
  3. Looper對象保存在ThreadLocal中的。ThreadLocal是一個線程內部的數據存儲類,每一個線程都有本身獨立訪問的變量副本。 libcore/ojluni/src/main/java/java/lang/ThreadLocal.java下有相關源碼。
  4. Looper建立,即建立了一個內部消息隊列mQueue,並綁定了當前線程。
  5. 主線程(UI線程)建立的Looper是不可退出的,應用啓動後默認建立好了的。(下面有單獨講解)

Looper循環:loop()

Message.java
@UnsupportedAppUsage
/*package*/ Handler target;

@UnsupportedAppUsage
/*package*/ Runnable callback;

Looper.java
public static void loop() {
    ......
    for (;;) {
        //不斷取出下一條消息,mgs爲null即消息隊列退出,若沒有消息且沒有退出 消息隊列一直阻塞的
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
        ......
        try {
            //分派消息,經過Handler處理
            msg.target.dispatchMessage(msg);
            if (observer != null) {
                observer.messageDispatched(token, msg);
            }
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } catch (Exception exception) {
        ......
        msg.recycleUnchecked();
    }
}

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

因爲代碼比較長,截取了關鍵代碼,......表示該處有省略的代碼。
loop()是消息循環運行的關鍵,總體把握這裏關注兩行代碼:Message msg = queue.next(); 和 msg.target.dispatchMessage(msg);。這兩個分別在MQ部分和Handler部分有詳述。多線程

  1. for(;;)死循環,這是關鍵,真正的消息循環。跳出的惟一條件是queue.next()爲null,但實際只有Looper執行quit()才能達成這一條件(next()方法MQ中詳述)。
  2. next()不斷取出下一條消息,mgs爲null即消息隊列退出,循環中止。若沒有消息且沒有退出 消息隊列一直阻塞的
  3. 當next()返回一條消息,Looper調用msg.target.dispatchMessage(msg)進行分派處理。這裏的msg.target就是一個Handler,即調用了Handler的dispatchMessage()方法(Handler中詳述)。

Handler

Handler主要包含消息的發送和接收處理。less

Handler的建立

@UnsupportedAppUsage
final Looper mLooper;
final MessageQueue mQueue;
@UnsupportedAppUsage
final Callback mCallback;
final boolean mAsynchronous;

/**
 * @hide
 */
public Handler(@Nullable 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 " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

注意幾點:異步

  1. Handler有好幾種構造方法,一種是指定了Looper,另外一種沒指定。全部構造方法都基本肯定4個值:Hanlder關聯的Looper;關聯的MQ;關聯的Callback;是否異步。
  2. 上述是沒指定Looper最終的方法。從中能夠看到,沒有Looper就沒法建立Handler。

Handler發送消息

public final boolean post(@NonNull Runnable r) {
    return  sendMessageDelayed(getPostMessage(r), 0);
}

private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}

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

public boolean sendMessageAtTime(@NonNull 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);
}

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
                               long uptimeMillis) {
    msg.target = this;
    msg.workSourceUid = ThreadLocalWorkSource.getUid();

    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}
  1. Handler經過post()、postDelayed()、sendMessage()、sendMessageDelayed()等方法發送消息,最終都是走到了上面的sendMessageAtTime()中。
  2. Handler各類發送方式,最終到sendMessageAtTime()。這個過程,參數uptimeMillis 即 消息發處的絕對時間也就是when。
  3. post()等發送方法傳入的參數Runnable即消息的callback(msg.callback),在dispatchMessage中能夠看到。
  4. 發送消息,Handler僅是將消息加入消息隊列中(enqueueMessage()在MQ中詳述),這個消息隊列就是Handler在建立時綁定的Looper的內部消息隊列。

這裏都是使用SystemClock.uptimeMillis(),簡單說明下SystemClock.uptimeMillis()與System.currentTimeMillis()區別:
System.currentTimeMillis()是1970年1月1日(UTC)到如今的毫秒值。
SystemClock.uptimeMillis()是設備啓動到如今的時間的毫秒值。(不包括深度睡眠)
SystemClock.elapsedRealtime()是設備啓動到如今時間的毫秒值。(包括深度睡眠)
爲何基本都用SystemClock.uptimeMillis()做爲時間間隔的獲取方法呢?
System.currentTimeMillis()經過設置設備的時間是能夠改變的,這樣設置後 那些計劃的執行明顯會發生異常。async

Handler分派處理:dispatchMessage

@UnsupportedAppUsage
/*package*/ Handler target;

@UnsupportedAppUsage
/*package*/ Runnable callback;

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

private static void handleCallback(Message message) {
    message.callback.run();
}

public interface Callback {
    /**
     * @param msg A {@link android.os.Message Message} object
     * @return True if no further handling is desired
     */
    boolean handleMessage(@NonNull Message msg);
}

/**
 * Subclasses must implement this to receive messages.
 */
public void handleMessage(@NonNull Message msg) {
}

Looper循環經過queue.next()獲取到一條消息,再經過Handler的dispatchMessage()分派處理。

  1. 若msg.callback不爲空,即上述所說(Handler發送消息 部分)的是否傳入了Runnable參數,有則執行Runnable。(Handler發送消息時傳入)
  2. 若是msg.callback爲空,在Handler建立時指定了Callback參數,即實現了handleMessage()的類做爲參數,則直接執行handleMessage()。(Handler建立時傳入)
  3. 若上面兩種回調都不存在,可由handleMessage()處理。(建立的Handler重寫該方法)

MessageQueue

消息隊列MQ。主要列出Looper和Handler中提到的幾個關於MQ的重要過程。
消息隊列是單鏈表實現的,這屬於數據結構,瞭解的話能夠參考數據結構之隊列(Queue)

入隊:enqueueMessage()

boolean enqueueMessage(Message msg, long when) {
    //Handler爲空
    if (msg.target == null) {
        throw new IllegalArgumentException("Message must have a target.");
    }
    //消息標記是使用中
    if (msg.isInUse()) {
        throw new IllegalStateException(msg + " This message is already in use.");
    }
    
    //線程安全,同步
    synchronized (this) {
        //消息隊列已退出,被放棄
        if (mQuitting) {
            IllegalStateException e = new IllegalStateException(
                    msg.target + " sending message to a Handler on a dead thread");
            Log.w(TAG, e.getMessage(), e);
            msg.recycle();
            return false;
        }

        //下面開始是正在入隊操做
        msg.markInUse();//標記使用中
        msg.when = when;
        Message p = mMessages;//當前消息,也是隊首便可以理解爲第一個消息
        boolean needWake;
        //隊列是空或者msg比隊列中其餘消息要先執行,該msg做爲隊首入隊。
        if (p == null || when == 0 || when < p.when) {
            // New head, wake up the event queue if blocked.
            //加入隊首。p是指向以前隊首的,瞭解隊列鏈表實現很容易理解
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;
        } else {
            // Inserted within the middle of the queue.  Usually we don't have to wake
            // up the event queue unless there is a barrier at the head of the queue
            // and the message is the earliest asynchronous message in the queue.
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            //for循環,跳出時:p指向null,prev指向隊尾最後一個消息,即msg最後執行。
            //或者p指向第一個when大於msg的消息,prev則指向前面一個(最後一個when小於msg的消息)
            for (;;) {
                prev = p;
                p = p.next;
                if (p == null || when < p.when) {
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            //msg插入到對應的位置
            msg.next = p; // invariant: p == prev.next
            prev.next = msg;
        }

        // We can assume mPtr != 0 because mQuitting is false.
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

Handler發送消息,將消息加入了消息隊列,即上面的enqueueMessage的方法。
這個方法不難理解,能夠看添加的中文註釋。
這裏主要注意的是消息的處理時間,看入隊邏輯 能夠看出消息隊列是按消息處理時間排隊的。

next()方法

@UnsupportedAppUsage
Message next() {
    ......
    for (;;) {
        ......
        nativePollOnce(ptr, nextPollTimeoutMillis);

        synchronized (this) {
            // Try to retrieve the next message.  Return if found.
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            //同步屏障,若存在異步消息 則獲取的是第一個異步消息
            if (msg != null && msg.target == null) {
                // Stalled by a barrier.  Find the next asynchronous message in the queue.
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                //尚未到消息處理時間,設置阻塞時間nextPollTimeoutMillis,進入下次循環的時候會調用nativePollOnce(ptr, nextPollTimeoutMillis)
                if (now < msg.when) {
                    // Next message is not ready.  Set a timeout to wake up when it is ready.
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    //得到並返回消息
                    // Got a message.
                    mBlocked = false;
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                    msg.markInUse();
                    return msg;
                }
            } else {
                //沒有消息要處理,nextPollTimeoutMillis設置爲-1。
                // No more messages.
                nextPollTimeoutMillis = -1;
            }

            // Process the quit message now that all pending messages have been handled.
            //消息隊列退出,返回null
            if (mQuitting) {
                dispose();
                return null;
            }
            ......
        }
        ......
    }
}

這個方法比較複雜,代碼比較長。上面只截取了部分關鍵代碼,能夠看下添加的中文註釋,可以理解。
注意兩個地方:

  1. 消息屏障(同步屏障)。能夠經過MessageQueue.postSyncBarrier()設置,即msg.target爲null。這裏至關於異步優先。
  2. 僅當mQuitting爲true時,即消息隊列退出(quit()),next()才返回null。Looper的looper()循環才中止。

最後來看下消息隊列的退出

退出:quit()

void quit(boolean safe) {
    if (!mQuitAllowed) {
        throw new IllegalStateException("Main thread not allowed to quit.");
    }

    synchronized (this) {
        if (mQuitting) {
            return;
        }
        mQuitting = true;

        if (safe) {
            removeAllFutureMessagesLocked();
        } else {
            removeAllMessagesLocked();
        }

        // We can assume mPtr != 0 because mQuitting was previously false.
        nativeWake(mPtr);
    }
}

private void removeAllFutureMessagesLocked() {
    final long now = SystemClock.uptimeMillis();
    Message p = mMessages;
    if (p != null) {
        if (p.when > now) {
            removeAllMessagesLocked();
        } else {
            Message n;
            for (;;) {
                n = p.next;
                if (n == null) {
                    return;
                }
                if (n.when > now) {
                    break;
                }
                p = n;
            }
            p.next = null;
            do {
                p = n;
                n = p.next;
                p.recycleUnchecked();
            } while (n != null);
        }
    }
}

private void removeAllMessagesLocked() {
    Message p = mMessages;
    while (p != null) {
        Message n = p.next;
        p.recycleUnchecked();
        p = n;
    }
    mMessages = null;
}

這個也不復雜,簡單關注兩點:

  1. quit()有安全退出和非安全退出,二者的差異從上面也能看到。非安全退出removeAllMessagesLocked(),直接退出 清空隊列消息;安全退出removeAllFutureMessagesLocked(),消息處理時間在如今now以後的消息會被直接清空,而在now以前的會繼續保留 由next()繼續獲取處理。
  2. quit()調用後纔有mQuitting = true,這樣next()纔會返回null,最終Looper.looper()循環纔會中止。

其餘注意點

Handler通常使用

Handler使用,通常是子線程進入主線程更新UI。下面是常見的操做。
主要注意Hanler的建立(多種方式的選擇)以及回調的處理,發送消息的方式。

private final String TAG = "HandlerActivity";

private final int MAIN_HANDLER_1 = 1;

private Handler mMainHandler = new Handler() {
    @Override
    public void handleMessage(@NonNull Message msg) {
        super.handleMessage( msg );
        switch (msg.what) {
            case  MAIN_HANDLER_1:
                //Do something. like Update UI
                break;
        }
    }
};;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate( savedInstanceState );
    Log.d( TAG, "onCreate: MainHandler Looper=" + mMainHandler.getLooper() );
    SubThread subThread = new SubThread();
    subThread.start();
}

private class SubThread extends  Thread {
    @Override
    public void run() {
        //Do something
        Message message = mMainHandler.obtainMessage();
        message.what = MAIN_HANDLER_1;
        mMainHandler.sendMessage(message);
    }
}

消息池

Message內部保存了一個緩存的消息池,咱們能夠經過Message.obtain()或者mMainHandler.obtainMessage()從緩存池得到一個消息對象。避免每次建立Message帶來的資源佔用。
Message.obtain()的多種方法以及mMainHandler.obtainMessage()最終都是調用obtain()從消息池中獲取一個消息對象。

public static Message obtain() {
    synchronized (sPoolSync) {
        if (sPool != null) {
            Message m = sPool;
            sPool = m.next;
            m.next = null;
            m.flags = 0; // clear in-use flag
            sPoolSize--;
            return m;
        }
    }
    return new Message();
}

子線程到主線程的方法

Handler的一個重要做用就是子線程進入主線程更新UI。
Android中的多進程、多線程也提到過2種
handler1
Activity.runOnUiThread(Runnable);View.post(Runnable)/View.postDelayed(Runnable, long)。
這2種方法其實就是Handler機制實現的。

Activity.java
public final void runOnUiThread(Runnable action) {
    if (Thread.currentThread() != mUiThread) {
        mHandler.post(action);
    } else {
        action.run();
    }
}

View.java
public boolean post(Runnable action) {
    final AttachInfo attachInfo = mAttachInfo;
    if (attachInfo != null) {
        return attachInfo.mHandler.post(action);
    }

    // Postpone the runnable until we know on which thread it needs to run.
    // Assume that the runnable will be successfully placed after attach.
    getRunQueue().post(action);
    return true;
}

private HandlerActionQueue getRunQueue() {
    if (mRunQueue == null) {
        mRunQueue = new HandlerActionQueue();
    }
    return mRunQueue;
}

@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
    ......
    // Transfer all pending runnables.
    if (mRunQueue != null) {
        mRunQueue.executeActions( info.mHandler );
        mRunQueue = null;
    }
    ......
}

HandlerActionQueue.java
public void executeActions(Handler handler) {
    synchronized (this) {
        final HandlerAction[] actions = mActions;
        for (int i = 0, count = mCount; i < count; i++) {
            final HandlerAction handlerAction = actions[i];
            handler.postDelayed(handlerAction.action, handlerAction.delay);
        }

        mActions = null;
        mCount = 0;
    }
}

主線程的Looper

ActivityThread中的main()是主線程的入口。
從下面代碼中能夠看出來,應用啓動 主線程默認建立了Looper,它是不可退出的。Looper有單獨保存並獲取主線程Looper的方法。
主線程Looper建立參數爲false(prepare(false)),即looper()的循環是不會中止的,當沒有消息時,一直是阻塞的。

Run|Debug
public static void main(String[] args) {
    ......
    Looper.prepareMainLooper();
    ......
    // End of event ActivityThreadMain.
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    /// M: ANR Debug Mechanism
    mAnrAppManager.setMessageLogger(Looper.myLooper());
    Looper.loop();

    throw new RuntimeException("Main thread loop unexpectedly exited");
}

Looper.java
public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

ANR問題

looper()死循環爲何沒致使ANR?ANR具體什麼形成?ANR和Looper有什麼關係? ---這篇已通過長,這些問題在ANR部分總結更好。

相關文章
相關標籤/搜索