Android源碼分析之消息機(jī)制Handler

1 前言

Handler在Android開發(fā)中使用的比較多,通常Handler是在主線程中創(chuàng)建,子線程拿到這個(gè)Handler向主線程中發(fā)送消息。

那么如果需要主線程中向子線程中發(fā)送消息呢?

本文只是提出這個(gè)問題,并不在此提供實(shí)現(xiàn)這種場(chǎng)景的例子,寫這篇文章的目的主要是理解Handler的運(yùn)行原理,以便更好、更靈活的運(yùn)用Handler。

2 Android消息機(jī)制簡介

  1. Android的消息機(jī)制主要是指Handler的運(yùn)行機(jī)制,Handler的運(yùn)行依賴于MessageQueue和Looper,當(dāng)然,既然是消息機(jī)制,通常也需要用到Message。
  2. Handler、Looper、MessageQueue和Message的工作原理就像是生產(chǎn)線,Looper是發(fā)動(dòng)機(jī),MessageQueue就是傳送帶,Handler是工人,Message就是待處理的產(chǎn)品

3 Handler的工作流程

一個(gè)應(yīng)用的啟動(dòng)需要有main方法作為啟動(dòng)的入口,在Android中這個(gè)main方法在ActivityThread類中,查看這個(gè)類的main方法可以看到:

public static final void main(String[] args) {
        //省略無關(guān)代碼
        ...
        //主線程中調(diào)用Looper.prepareMainLooper()方法創(chuàng)建Looper
        Looper.prepareMainLooper();
        if (sMainThreadHandler == null) {
            sMainThreadHandler = new Handler();
        }

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }
    //主線程中調(diào)用Looper.loop(),開始輪詢,取消息
        Looper.loop();

        if (Process.supportsProcesses()) {
            throw new RuntimeException("Main thread loop unexpectedly exited");
        }

       ...
    }
}

3.1 Looper輪詢器

在ActivityThread的main方法中首先調(diào)用Looper.prepareMainLooper()方法,我們來看一下,這個(gè)方法中做了哪些工作:

在Looper類中
public static final void prepareMainLooper() {
     prepare();
     setMainLooper(myLooper());
     if (Process.supportsProcesses()) {
        myLooper().mQueue.mQuitAllowed = false;
     }
}

該方法中首先調(diào)用本類中的prepare()方法,這個(gè)方法的主要作用是創(chuàng)建Looper對(duì)象,并且把該對(duì)象綁定到當(dāng)前線程中,在這里既然是主線程調(diào)用的,那么該Looper對(duì)象也就是在主線程當(dāng)中

在Looper類中
public static final void prepare() {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    //把Looper綁定到當(dāng)前線程
    sThreadLocal.set(new Looper());
}

在Looper的構(gòu)造方法中,我們可以看到:

private Looper() {
    //創(chuàng)建消息隊(duì)列
    mQueue = new MessageQueue();
    mRun = true;
    mThread = Thread.currentThread();
}

首先,Looper這個(gè)類的構(gòu)造方法是私有的,也就是說不允許外界直接new出來Looper對(duì)象。在創(chuàng)建Looper的時(shí)候,同時(shí)創(chuàng)建了一個(gè)消息隊(duì)列MessageQueue。

所以請(qǐng)注意,消息隊(duì)列MessageQueue是在Looper創(chuàng)建的時(shí)候,同時(shí)創(chuàng)建的

通過調(diào)用Looper.loop()方法,開始輪詢消息:

   public static final void loop() {
        Looper me = myLooper();
        //通過拿到Looper對(duì)象,取獲取與之對(duì)應(yīng)的消息隊(duì)列MessageQueue
        MessageQueue queue = me.mQueue;
       ...
        //通過死循環(huán)方式去取消息
        while (true) {
            //調(diào)用MessageQueue的next()方法取消息,這個(gè)過程也是死循環(huán)
            Message msg = queue.next(); // might block
            
            if (msg != null) {
                ...
                //取到消息之后,交給發(fā)送該消息的Handler取處理消息
                msg.target.dispatchMessage(msg);
               ...
                //回收消息,在Message中維護(hù)的有一個(gè)消息池
                msg.recycle();
            }
        }
    }

通過上述簡單的敘述,我們可以明白,在Activity啟動(dòng)的時(shí)候創(chuàng)建的一個(gè)與主線程相關(guān)的Looper和對(duì)應(yīng)的消息隊(duì)列MessageQueue,并且通過消息隊(duì)列通過阻塞的方式去取消息。

3.2 Handler的工作過程

Handler的工作過程主要包含發(fā)送消息和處理消息。Handler的構(gòu)造方法有多個(gè),他們的共同點(diǎn)是均會(huì)獲取當(dāng)前線程的Looper對(duì)象和消息隊(duì)列MessageQueue對(duì)象

//Handler的構(gòu)造方法1
public Handler() {
    ...
    //獲取當(dāng)前線程的Looper對(duì)象
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    //獲取消息隊(duì)列對(duì)象
    mQueue = mLooper.mQueue;
    mCallback = null;
}

//Handler的構(gòu)造方法2
public Handler(Callback callback) {
   ...
    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;
}

//Handler的構(gòu)造方法3
public Handler(Looper looper) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = null;
}

//Handler的構(gòu)造方法4
public Handler(Looper looper, Callback callback) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
}

發(fā)送消息可以通過一系列send方法,也可以通過一系列post方法,不過post方法最終還是通過send方法去實(shí)現(xiàn)。
用send方法,最終也會(huì)調(diào)用一個(gè)方法如下:

//在Handler中
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
{
    boolean sent = false;
    MessageQueue queue = mQueue;
    if (queue != null) {
        //調(diào)用msg.target的方法把Message和發(fā)送它的Handler綁定,所以Looper能夠把正確的Message交給發(fā)送它的Handler處理
        msg.target = this;
        //調(diào)用MessageQueue的enqueueMessage方法把消息加入消息隊(duì)列
        sent = queue.enqueueMessage(msg, uptimeMillis);
    }
    else {
        RuntimeException e = new RuntimeException(
            this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
    }
    return sent;
}

可以看到,Handler發(fā)送消息的過程就是向消息隊(duì)列中插入一條消息。前面已經(jīng)講到,MessageQueue調(diào)用next方法去輪詢消息,那么當(dāng)MessageQueue拿到消息之后,把消息傳遞給Looper,最終交給Handler去處理,即dispatchMessage方法會(huì)被調(diào)用。此時(shí),Handler開始處理消息。值得一提的是,在消息隊(duì)列中可能有不同Handler發(fā)送的多個(gè)消息,通過在發(fā)送消息的時(shí)候把Message和發(fā)送它的Handler綁定,Looper就會(huì)把消息正確的交給發(fā)送它的Handler來處理。dispatchMessage方法如下:

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

Handler處理消息的過程如下:

  1. 查看Message的callback是否為null,不為null的話就通過handleCallback(msg)方法處理消息。這里的callback實(shí)際上就是一個(gè)Runnable對(duì)象,如果以post方式去發(fā)送消息,最終就會(huì)調(diào)用handleCallback(msg)方法去處理,這個(gè)方法內(nèi)容為:

     private final void handleCallback(Message message) {
         message.callback.run();
     }
    
  2. 檢查mCallBack是否為null,如果不為null就調(diào)用mCallBack的handleMessage(msg)方法。這個(gè)mCallBack是CallBack接口的子類對(duì)象,前面已經(jīng)說過Handler的構(gòu)造方法中有兩個(gè)可以用到這個(gè)CallBack。

  3. 如果mCallBack為null,最終就會(huì)調(diào)用Handler的handleMessage(msg)方法,這個(gè)方法通常是在創(chuàng)建Handler時(shí)被使用者重寫的方法。

需要說明的是:在主線程當(dāng)前我們創(chuàng)建Handler時(shí)并沒有自己創(chuàng)建Looper對(duì)象,這是因?yàn)橹骶€程已經(jīng)為我們創(chuàng)建好了;如果要在子線程當(dāng)前創(chuàng)建Handler,一定要在之前創(chuàng)建Looper對(duì)象,即調(diào)用Looper.prepare()方法。

3.3 MessageQueue消息隊(duì)列

前面的內(nèi)容已經(jīng)講了很多關(guān)于MessageQueue的東西,這里也就不再贅述,MessageQueue主要包含兩個(gè)操作:插入消息(enqueueMessage方法)和讀取消息(next方法)。讀取的過程也伴隨著刪除操作。MessageQueue的的內(nèi)部實(shí)際上是通過一個(gè)單鏈表的數(shù)據(jù)結(jié)構(gòu)來維護(hù)消息列表,這主要也是因?yàn)閱捂湵碓诓迦牒蛣h除上比較有優(yōu)勢(shì)。

   final boolean enqueueMessage(Message msg, long when) {
    ...
        Message p = mMessages;
        if (p == null || when == 0 || when < p.when) {
            // 當(dāng)前發(fā)送的message需要馬上被處理調(diào),needWake喚醒狀態(tài)置true
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked; // new head, might need to wake up
         } else {
              // 當(dāng)前發(fā)送的message被排隊(duì)到其他message的后面,needWake喚醒狀態(tài)置為false
                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);
    }
    return true;
}

enqueueMessage的主要操作其實(shí)就是單鏈表的插入操作,根據(jù)時(shí)間看當(dāng)前發(fā)送的Message是否需要馬上處理。這個(gè)enqueueMessage方法是Handler發(fā)送消息的時(shí)候調(diào)用。

下面來看next方法:

final Message next() {
    int pendingIdleHandlerCount = -1; // -1 only during first iteration
    int nextPollTimeoutMillis = 0;

    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
        nativePollOnce(mPtr, nextPollTimeoutMillis);

        synchronized (this) {
            // Try to retrieve the next message.  Return if found.
            final long now = SystemClock.uptimeMillis();
            final Message msg = mMessages;
            if (msg != null) {
                final long when = msg.when;
                if (now >= when) {
                    mBlocked = false;
                    mMessages = msg.next;
                    msg.next = null;
                    if (Config.LOGV) Log.v("MessageQueue", "Returning message: " + msg);
                    return msg;
                } else {
                    nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);
                }
            } else {
                nextPollTimeoutMillis = -1;
            }

        ...
}

可以看到,next方法返回一個(gè)Message對(duì)象。next方法中也是采用阻塞的方式去獲取消息隊(duì)列中的消息,一旦有消息立即返回并且將它從單鏈表中移除。如果沒有消息就一直阻塞。前面已經(jīng)提到,這個(gè)取消息的next方法是在Looper的loop()方法中調(diào)用。

3.4 Message消息載體

Message只有一個(gè)無參構(gòu)造方法,但是Message有多個(gè)obtain靜態(tài)方法來返回Message對(duì)象。

采用哪種方式創(chuàng)建Message對(duì)象都可以,但是建議采用obtain方法來創(chuàng)建。這是因?yàn)镸essage通過在內(nèi)部構(gòu)建一個(gè)鏈表來維護(hù)一個(gè)被回收的Message對(duì)象池。當(dāng)用戶調(diào)用obtain方法時(shí)會(huì)優(yōu)先從池中獲取,如果池中沒有則創(chuàng)建新的Message對(duì)象。同時(shí)在使用完畢之后,進(jìn)入池中以便于復(fù)用。這個(gè)在Looper.loop()方法可以看到一點(diǎn)端倪,在使用完畢時(shí)候調(diào)用了Message的recycle()方法。
下面是obtain方法創(chuàng)建Message對(duì)象的流程:

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

4 小結(jié)

  • 在應(yīng)用啟動(dòng)時(shí),會(huì)開啟一個(gè)主線程(即UI線程),會(huì)創(chuàng)建一個(gè)Looper對(duì)象,并把該對(duì)象綁定到主線程中。Looper對(duì)象被封裝在ThreadLocal中,使得不同線程間的Looper不能共享
  • 創(chuàng)建Looper時(shí)同時(shí)創(chuàng)建了MessageQueue消息隊(duì)列對(duì)象
  • Handler通過send方法或者post方法,把消息加入消息隊(duì)列MessageQueue中
  • 主線程中調(diào)用Looper的loop()方法,會(huì)開啟消息循環(huán),不斷的從消息隊(duì)列中取出消息
  • Looper拿到消息之后調(diào)用Handler的dispatchMessage方法來處理消息
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容