Android Handler分析

我們經常說的Handler機制是一套包含Handler,Looper,MessageQueue的異步消息處理系統,今天就分析一下這套機制的原理。

1.相關概念
1)Message
定義:消息,理解為線程間通訊的數據單元(Handler接受和處理的消息對象。)
2)Message Queue
定義:消息隊列
作用:用來存放通過Handler發過來的消息,按照先進先出執行
提供enqueueMessage 方法,將消息根據時間放置到隊列中;
提供next方法,從隊列中獲取消息,沒有消息的時候阻塞。
3)Handler
定義:Handler是Message的主要處理者
作用:負責將Message添加到消息隊列&處理Looper分派過來的Message
提供sendMessage方法,將消息放置到隊列中
提供handleMessage方法,定義個各種消息的處理方式;
4)Looper
定義:循環器,扮演Message Queue和Handler之間橋梁的角色
作用:主要負責消息循環:循環取出Message Queue的Message;消息派發:將取出的Message交付給相應的Handler。
Looper.prepare():實例化Looper對象;為當前線程生成一個消息隊列;
Looper.loop() :循環從消息隊列中獲取消息,交給Handler處理;此時線程處于無限循環中,不停的從MessageQueue中獲取Message 消息 ;如果沒有消息就阻塞。

    幾個需要注意的地方:
    1)每個線程中只能擁有一個Looper,但是一個Looper可以和多個線程的Handler綁定起來,也就是說很多個線程可以往一個Looper所持有的MessageQueue中發送消息。這就給我們提供了線程之間通信的可能。
    2)Handler在創建的時候可以顯示指定Looper,這樣在Handler在調用sendMessage()投遞消息的時候會將消息添加到指定的Looper里面的MessageQueue。如果不指定Looper,Handler默認綁定的是創建它的線程的Looper。
  下面是Handler機制運行圖:
944365-783bc46b43828acd.png
  工作流程圖解釋:
  異步通信傳遞機制步驟主要包括異步通信的準備、消息發送、消息循環和消息處理
  1)異步通信的準備
   包括Looper對象的創建&實例化、MessageQueue隊列的創建和Handler的實例化。
  2)消息發送
  Handler將消息發送到消息隊列中
  3)消息循環
  Looper執行Looper.loop()進入消息循環,在這個循環過程中,不斷從該Message Queue取出消息,并將取出的消息派發給創建該消息的Handler
  4)消息處理
  調用該Handler的dispatchMessage(msg)方法,即回調handleMessage(msg)處理消息。

2.工作流程詳解
(1)Looper
Looper主要負責:
1)自身的創建&創建Message Queue
2)消息循環(消息取出、派發)
對應職責我們來看下相應的源碼:
1)自身的創建&創建Message Queue:prepare()方法

public static final void prepare() {
//判斷sThreadLocal是否為null,否則拋出異常
//即Looper.prepare()方法不能被調用兩次
//也就是說,一個線程中只能對應一個Looper實例
     if (sThreadLocal.get() != null) {
         throw new RuntimeException("Only one Looper may be created per thread");
     }
//sThreadLocal是一個ThreadLocal對象,用于在一個線程中存儲變量
//實例化Looper對象并存放在ThreadLocal
//這說明Looper是存放在Thread線程里的
     sThreadLocal.set(new Looper(true));
}
//再來看下Looper的構造方法
private Looper(boolean quitAllowed) {
//創建了一個MessageQueue(消息隊列)
//這說明,當創建一個Looper實例時,會自動創建一個與之配對的MessageQueue(消息隊列)
     mQueue = new MessageQueue(quitAllowed);
     mRun = true;
     mThread = Thread.currentThread();
}
    2)消息循環:loop()方法
        ```

public static void loop() {
//myLooper()方法作用是返回sThreadLocal存儲的Looper實例,如果me為null,loop()則拋出異常
//也就是說loop方法的執行必須在prepare方法之后運行
//也就是說,消息循環必須要先在線程當中創建Looper實例
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//獲取looper實例中的mQueue(消息隊列)
final MessageQueue queue = me.mQueue;

    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();

//進入消息循環
for (;;) {
//next()方法用于取出消息隊列里的消息
//如果取出的消息為空,則線程阻塞
Message msg = queue.next(); // might block
if (msg == null) {

            return;
        }


        Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }

//消息派發:把消息派發給msg的target屬性,然后用dispatchMessage方法去處理
//Msg的target其實就是handler對象,下面會繼續分析
msg.target.dispatchMessage(msg);

        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }

        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.recycle();
}
}
```
總結一下Looper的作用:
1)實例化本身、與當前線程綁定、創建與之相應的MessageQueue:prepare()方法,一個線程只會有一個Looper實例,同時一個Looper實例也只有一個MessageQueue。
2)消息循環(消息取出、消息派發):loop()方法
不斷從MessageQueue中去取消息,派發給消息的target屬性的Handler,然后調用相應Handler的dispatchMessage()方法進行消息處理。

(2)Handler
Handler主要負責:
1)在子線程發送消息給MessageQueue
2)處理Looper派發過來的消息
Handler是需要和線程綁定在一起的,在初始化Handler的時候一般通過指定Looper對象從而綁定相應線程,即給Handler指定Looper對象=綁定到了Looper對象所在的線程中,Handler的消息處理回調會在那個線程中執行。一般有兩種方法創建:
1)通過Loop.myLooper()得到當前線程的Looper對象/通過Loop.getMainLooper()可以獲得當前進程的主線程的Looper對象。
2)不指定Looper對象,那么這個Handler綁定到了創建這個線程的線程上,消息處理回調也就在創建線程中執行。
首先看一下Handler的構造方法

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());
           }
       }
//通過Looper.myLooper()獲取了當前線程保存的Looper實例,如果線程沒有Looper實例那么會拋出異常
//這說明在一個沒有創建Looper的線程中是無法創建一個Handler對象的
//所以說我們在子線程中創建一個Handler時首先需要創建Looper,并且開啟消息循環才能夠使用這個Handler。
       mLooper = Looper.myLooper();
       if (mLooper == null) {
           throw new RuntimeException(
               "Can't create handler inside thread that has not called Looper.prepare()");
       }
//獲取了這個Looper實例中保存的MessageQueue(消息隊列)
//這樣就保證了handler的實例與我們Looper實例中MessageQueue關聯上了

       mQueue = mLooper.mQueue;
       mCallback = callback;
       mAsynchronous = async;
   }
  上述說明:當Handler初始化時,可通過構造方法自動關聯Looper和相應的MessageQueue。
   Handler發送消息有send和post兩種,下面分別看一下這兩種方法:

        1)send的發送方法:sendMessage()
public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }

//我們往下扒
   public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }


 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
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
//調用了enqueueMessage方法
        return enqueueMessage(queue, msg, uptimeMillis);
    }

//調用sendMessage方法其實最后是調用了enqueueMessage方法
 private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//為msg.target賦值為this,也就是把當前的handler作為msg的target屬性
//如果大家還記得Looper的loop()方法會取出每個msg然后執行msg.target.dispatchMessage(msg)去處理消息,其實就是派發給相應的Handler
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
//最終調用queue的enqueueMessage的方法,也就是說handler發出的消息,最終會保存到消息隊列中去。
        return queue.enqueueMessage(msg, uptimeMillis);
    }
      2)Post的發送方法:sendMessage()
  ```

showhandler.post(new Runnable() {
@Override
public void run() {
String line = "\n";
StringBuffer text = new StringBuffer(show.getText());
text.append(line).append("angelababy:Yes,I do");
show.setText(text);
}

     相比send方法,post方法最大的不同在于,更新UI操作可直接在重寫的run方法定義。
    其實Runnable并沒有創建什么線程,而是發送了一條消息,下面看源碼:

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

private static Message getPostMessage(Runnable r) {
//創建了一個Message對象
//創建Message對象可以new,也可以使用Message.obtain()方法;
//但是更建議使用obtain方法,因為Message內部維護了一個Message池用于Message的復用,避免使用new 重新分配內存。
Message m = Message.obtain();
//將我們創建的Runable對象作為callback屬性,賦值給了此message.
m.callback = r;
//創建了一個Message對象
return m;
}

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

        從上面的源碼發現了吧?和send中的handler.sendMessage是一樣的。調用了sendMessageAtTime,然后調用了enqueueMessage方法,給msg.target賦值為handler,最終Handler將消息加入MessagQueue。
        但是細心的你會發現,在使用Post方法時會將我們創建的Runable對象作為callback屬性賦值給了此message,那么msg的callback和target都有值,那么會執行哪個呢?我們已知回調發送消息的方法是:dispatchMessage()

public void dispatchMessage(Message msg) {
//一開始就會進行判斷
//如果msg.callback屬性不為null,則執行callback回調,也就是我們的Runnable對象
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}

      可以看到dispathMessage()方法里調用了 handleMessage()方法,但handleMessage()是一個空方法。
      因為Handler發送消息過來是希望進行一定的處理,至于怎么處理消息是該Handler最終控制的,所以我們在創建handler時需要通過復寫handleMessage()方法從而實現我們需要的消息處理方式,然后根據msg.what標識進行消息處理。
       需要注意的地方:
在一個Android應用啟動的時候,會創建一個主線程,即ActivityThread(也叫UI線程),在ActivityThread中有一個靜態的main方法:應用程序的入口點。

//一個進程會默認生成一個主線程
public static void main(String[] args) {
......
//主線程生成時自動通過prepareMainLooper方法為主線程創建一個Looper
//prepare()方法是用于在子線程中創建一個Looper對象,在子線程中是可以退出消息循環的:調用消息隊列的quit方法
//Looper生成時會自動生成與之配套的消息隊列
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
......
//loop()方法開啟消息循環
//主線程的消息循環是不允許被退出的
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}

   (3)MessageQueue
          即消息隊列,用于存放Handler發送過來的消息,為了提高插入刪除的效率,采用單鏈表的方式實現。下面分別看一下MessageQueue的入隊和出隊操作。
         1)MessageQueue入隊

boolean enqueueMessage(Message msg, long when) {

......

synchronized (this) {

    ......

    msg.markInUse();
    msg.when = when;
    Message p = mMessages;
    boolean needWake;
    if (p == null || when == 0 || when < p.when) {
        // New head, wake up the event queue if blocked.
        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 (;;) {
            prev = p;
            p = p.next;
            if (p == null || when < p.when) {
                break;
            }
            if (needWake && p.isAsynchronous()) {
                needWake = false;
            }
        }
        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;

}

        消息的入隊(插入)過程
         首先判斷消息隊列里有沒有消息,沒有的話則將當前插入的消息作為隊頭,并且這時消息隊列如果處于等待狀態的話則將其喚醒。
        若是在中間插入,則根據Message創建的時間進行插入。
        
        2)MessageQueue出隊

Message next() {

......

int nextPollTimeoutMillis = 0;
for (;;) {
    if (nextPollTimeoutMillis != 0) {
        Binder.flushPendingCommands();
    }

// nativePollOnce方法在native層,若是nextPollTimeoutMillis為-1,這時候消息隊列處于等待狀態。   
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) {
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,下次循環消息隊列則處于等待狀態
nextPollTimeoutMillis = -1;
}

//退出消息隊列,返回null,這時候Looper中的消息循環也會終止。
if (mQuitting) {
dispose();
return null;
}

        ......
    }

    .....
}

}


參考文章:http://www.lxweimin.com/p/9fe944ee02f7

 

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容