Android消息機制1-Handler(Java層)

相關源碼java

framework/base/core/java/andorid/os/Handler.java
framework/base/core/java/andorid/os/Looper.java
framework/base/core/java/andorid/os/Message.java
framework/base/core/java/andorid/os/MessageQueue.java
libcore/luni/src/main/java/java/lang/ThreadLocal.java

1、概述

在整個Android的源碼世界裏,有兩大利劍,其一是Binder IPC機制,,另外一個即是消息機制(由Handler/Looper/MessageQueue等構成的)。關於Binder在Binder系列中詳細講解過,有興趣看看。git

Android有大量的消息驅動方式來進行交互,好比Android的四劍客ActivityServiceBroadcastContentProvider的啓動過程的交互,都離不開消息機制,Android某種意義上也能夠說成是一個以消息驅動的系統。消息機制涉及MessageQueue/Message/Looper/Handler這4個類。安全

1.1 模型

消息機制主要包含:架構

  • Message:消息分爲硬件產生的消息(如按鈕、觸摸)和軟件生成的消息;
  • MessageQueue:消息隊列的主要功能向消息池投遞消息(MessageQueue.enqueueMessage)和取走消息池的消息(MessageQueue.next);
  • Handler:消息輔助類,主要功能向消息池發送各類消息事件(Handler.sendMessage)和處理相應消息事件(Handler.handleMessage);
  • Looper:不斷循環執行(Looper.loop),按分發機制將消息分發給目標處理者。

1.2 架構圖

handler_java

  • Looper有一個MessageQueue消息隊列;
  • MessageQueue有一組待處理的Message;
  • Message中有一個用於處理消息的Handler;
  • Handler中有Looper和MessageQueue。

1.3 典型實例

先展現一個典型的關於Handler/Looper的線程異步

class LooperThread extends Thread {
    public Handler mHandler;
  
    public void run() {
        Looper.prepare();   //【見 2.1】
  
        mHandler = new Handler() {  //【見 3.1】
            public void handleMessage(Message msg) {
                //TODO    定義消息處理邏輯. 【見 3.2】
            }
        };
  
        Looper.loop();  //【見 2.2】
    }
}

接下來,圍繞着這個實例展開詳細分析。async

2、Looper

2.1 prepare()

對於無參的狀況,默認調用prepare(true),表示的是這個Looper運行退出,而對於false的狀況則表示當前Looper不運行退出。ide

private static void prepare(boolean quitAllowed) {
    //每一個線程只容許執行一次該方法,第二次執行時線程的TLS已有數據,則會拋出異常。
    if (sThreadLocal.get() != null) { 
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    //建立Looper對象,並保存到當前線程的TLS區域
    sThreadLocal.set(new Looper(quitAllowed));   
}

這裏的sThreadLocal是ThreadLocal類型,下面,先說說ThreadLocal。oop

ThreadLocal: 線程本地存儲區(Thread Local Storage,簡稱爲TLS),每一個線程都有本身的私有的本地存儲區域,不一樣線程之間彼此不能訪問對方的TLS區域。TLS經常使用的操做方法:post

  • ThreadLocal.set(T value):將value存儲到當前線程的TLS區域,源碼以下:ui

    public void set(T value) {
          Thread currentThread = Thread.currentThread(); //獲取當前線程
          Values values = values(currentThread); //查找當前線程的本地儲存區
          if (values == null) {
              //當線程本地存儲區,還沒有存儲該線程相關信息時,則建立Values對象
              values = initializeValues(currentThread); 
          }
          //保存數據value到當前線程this
          values.put(this, value);
      }
  • ThreadLocal.get():獲取當前線程TLS區域的數據,源碼以下:

    public T get() {
          Thread currentThread = Thread.currentThread(); //獲取當前線程
          Values values = values(currentThread); //查找當前線程的本地儲存區
          if (values != null) {
              Object[] table = values.table;
              int index = hash & values.mask;
              if (this.reference == table[index]) {
                  return (T) table[index + 1]; //返回當前線程儲存區中的數據
              }
          } else {
              //建立Values對象
              values = initializeValues(currentThread);
          }
          return (T) values.getAfterMiss(this); //從目標線程存儲區沒有查詢是則返回null
      }

ThreadLocal的get()和set()方法操做的類型都是泛型,接着回到前面提到的sThreadLocal變量,其定義以下:

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>()

可見sThreadLocal的get()和set()操做的類型都是Looper類型。

Looper.prepare()

Looper.prepare()在每一個線程只容許執行一次,該方法會建立Looper對象,Looper的構造方法中會建立一個MessageQueue對象,再將Looper對象保存到當前線程TLS。

對於Looper類型的構造方法以下:

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);  //建立MessageQueue對象. 【見4.1】
    mThread = Thread.currentThread();  //記錄當前線程.
}

另外,與prepare()相近功能的,還有一個prepareMainLooper()方法,該方法主要在ActivityThread類中使用。

public static void prepareMainLooper() {
    prepare(false); //設置不容許退出的Looper
    synchronized (Looper.class) {
        //將當前的Looper保存爲主Looper,每一個線程只容許執行一次。
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

2.2 loop()

public static void loop() {
    final Looper me = myLooper();  //獲取TLS存儲的Looper對象 【見2.4】
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;  //獲取Looper對象中的消息隊列

    Binder.clearCallingIdentity();
    //確保在權限檢查時基於本地進程,而不是基於最初調用進程。
    final long ident = Binder.clearCallingIdentity(); 

    for (;;) { //進入loop的主循環方法
        Message msg = queue.next(); //可能會阻塞 【見4.2】
        if (msg == null) { //沒有消息,則退出循環
            return;
        }
        
        Printer logging = me.mLogging;  //默認爲null,可經過setMessageLogging()方法來指定輸出,用於debug功能
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }
        msg.target.dispatchMessage(msg); //用於分發Message 【見3.2】
        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }

        final long newIdent = Binder.clearCallingIdentity(); //確保分發過程當中identity不會損壞
        if (ident != newIdent) {
             //打印identity改變的log,在分發消息過程當中是不但願身份被改變的。
        }
        msg.recycleUnchecked();  //將Message放入消息池 【見5.2】
    }
}

loop()進入循環模式,不斷重複下面的操做,直到沒有消息時退出循環

  • 讀取MessageQueue的下一條Message;
  • 把Message分發給相應的target;
  • 再把分發後的Message回收到消息池,以便重複利用。

這是這個消息處理的核心部分。另外,上面代碼中能夠看到有logging方法,這是用於debug的,默認狀況下logging == null,經過設置setMessageLogging()用來開啓debug工做。

2.3 quit()

public void quit() {
    mQueue.quit(false); //消息移除 
}

public void quitSafely() {
    mQueue.quit(true); //安全地消息移除
}

Looper.quit()方法的實現最終調用的是MessageQueue.quit()方法

MessageQueue.quit()

void quit(boolean safe) {
        // 當mQuitAllowed爲false,表示不運行退出,強行調用quit()會拋出異常
        if (!mQuitAllowed) {
            throw new IllegalStateException("Main thread not allowed to quit.");
        }
        synchronized (this) {
            if (mQuitting) { //防止屢次執行退出操做
                return;
            }
            mQuitting = true;
            if (safe) {
                removeAllFutureMessagesLocked(); //移除還沒有觸發的全部消息
            } else {
                removeAllMessagesLocked(); //移除全部的消息
            }
            //mQuitting=false,那麼認定爲 mPtr != 0
            nativeWake(mPtr);
        }
    }

消息退出的方式:

  • 當safe =true時,只移除還沒有觸發的全部消息,對於正在觸發的消息並不移除;
  • 當safe =flase時,移除全部的消息

2.4 經常使用方法

myLooper()

用於獲取TLS存儲的Looper對象

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

post()

發送消息,並設置消息的callback,用於處理消息。

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

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

3、Handler

3.1 new Handler()

(1)無參構造方法

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

public Handler(Callback callback, boolean async) {
    //匿名類、內部類或本地類都必須申明爲static,不然會警告可能出現內存泄露
    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.prepare(),才能獲取Looper對象,不然爲null.
    mLooper = Looper.myLooper();  //從當前線程的TLS中獲取Looper對象【見2.1】
    if (mLooper == null) {
        throw new RuntimeException("");
    }
    mQueue = mLooper.mQueue; //消息隊列,來自Looper對象
    mCallback = callback;  //回調方法
    mAsynchronous = async; //設置消息是否爲異步處理方式
}

對於Handler的無參構造方法,默認採用當前線程TLS中的Looper對象,而且callback回調方法爲null,且消息爲同步處理方式。只要執行的Looper.prepare()方法,那麼即可以獲取有效的Looper對象。

(2) 帶參數Looper的構造方法

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

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

Handler類在構造方法中,可指定Looper,Callback回調方法以及消息的處理方式(同步或異步),對於無參的handler,默認是當前線程的Looper。

3.2 dispatchMessage

在Looper.loop()中,當發現有消息時,調用消息的目標handler,執行dispatchMessage()方法來分發消息。

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        //當Message存在回調方法,回調msg.callback.run()方法;
        handleCallback(msg);  
    } else {
        if (mCallback != null) {
            //當Handler存在Callback成員變量時,回調方法handleMessage();
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        //Handler自身的回調方法handleMessage()
        handleMessage(msg); 
    }
}

分發消息流程:

  1. Message的回調方法不爲空時,則回調方法msg.callback.run(),其中callBack數據類型爲Runnable,不然進入步驟2;
  2. Handler存在mCallback成員變量不爲空時,則回調方法mCallback.handleMessage(msg),不然進入步驟3;
  3. 調用Handler自身的回調方法handleMessage(),該方法默認爲空,Handler子類經過覆寫該方法來完成具體的邏輯。

對於不少狀況下,消息分發後的處理方法是第3種狀況,即Handler.handleMessage(),通常地每每經過覆寫該方法從而實現本身的業務邏輯。

3.3 sendMessage

發送消息調用鏈:

java_sendmessage

從上圖,能夠發現全部的發消息方式,最終都是調用MessageQueue.enqueueMessage();

(1) sendEmptyMessage

public final boolean sendEmptyMessage(int what)
{
    return sendEmptyMessageDelayed(what, 0);
}

(2) sendEmptyMessageDelayed

public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
    Message msg = Message.obtain();
    msg.what = what;
    return sendMessageDelayed(msg, delayMillis);
}

(3) sendMessageDelayed

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

(4) sendMessageAtTime

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
    if (queue == null) {
        return false;
    }
    return enqueueMessage(queue, msg, uptimeMillis);
}

(5) sendMessageAtFrontOfQueue

public final boolean sendMessageAtFrontOfQueue(Message msg) {
    MessageQueue queue = mQueue;
    if (queue == null) {
        return false;
    }
    return enqueueMessage(queue, msg, 0);
}

該方法經過設置消息的觸發時間爲0,從而使Message加入到消息隊列的隊頭。

(6) enqueueMessage

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

Handler.sendEmptyMessage()方法,最終調用MessageQueue.enqueueMessage(msg, uptimeMillis),其中uptimeMillis爲系統當前的運行時間,不包括休眠時間。

3.4 經常使用方法

post

發送消息

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

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

obtainMessage

獲取消息

public final Message obtainMessage()
{
    return Message.obtain(this); 【見5.2】
}

Handler.obtainMessage()方法,最終調用Message.obtainMessage(this),其中this爲當前的Handler對象。

removeMessages

public final void removeMessages(int what) {
    mQueue.removeMessages(this, what, null); 【見 4.5】
}

Handler相似於輔助類,更多的實現都是MessageQueue, Message中的方法。Handler的目的是爲了更加方便的使用消息機制。

4、MessageQueue

MessageQueue是消息機制的Java層和C++層的鏈接紐帶,大部分核心方法都交給native層來處理,其中MessageQueue類中涉及的native方法以下:

private native static long nativeInit(); 
private native static void nativeDestroy(long ptr); 
private native void nativePollOnce(long ptr, int timeoutMillis); 
private native static void nativeWake(long ptr);
private native static boolean nativeIsPolling(long ptr);
private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);

關於這些native方法的介紹,見Android消息機制2-Handler(native篇)

4.1 new MessageQueue()

MessageQueue(boolean quitAllowed) {
    mQuitAllowed = quitAllowed;
    //經過native方法初始化消息隊列,其中mPtr是供native代碼使用
    mPtr = nativeInit(); 
}

4.2 next()

提取下一條message

Message next() {
    final long ptr = mPtr;
    if (ptr == 0) { //當消息循環已經退出,則直接返回
        return null;
    }
    int pendingIdleHandlerCount = -1; // 循環迭代的首次爲-1
    int nextPollTimeoutMillis = 0;
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
        //阻塞操做,當等待nextPollTimeoutMillis時長,或者消息隊列被喚醒,都會返回
        nativePollOnce(ptr, nextPollTimeoutMillis); 
        synchronized (this) {
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            if (msg != null && msg.target == null) {
                //當消息Handler爲空時,查詢MessageQueue中的下一條異步消息msg,則退出循環。
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                if (now < msg.when) {
                    //當異步消息觸發時間大於當前時間,則設置下一次輪詢的超時時長
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // 獲取一條消息,並返回
                    mBlocked = false;
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    //設置消息的使用狀態,即flags |= FLAG_IN_USE
                    msg.markInUse(); 
                    return msg;   //成功地獲取MessageQueue中的下一條即將要執行的消息
                }
            } else {
                //沒有消息
                nextPollTimeoutMillis = -1;
            }
            //消息正在退出,返回null
            if (mQuitting) {
                dispose();
                return null;
            }
            //當消息隊列爲空,或者是消息隊列的第一個消息時
            if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) {
                pendingIdleHandlerCount = mIdleHandlers.size();
            }
            if (pendingIdleHandlerCount <= 0) {
                //沒有idle handlers 須要運行,則循環並等待。
                mBlocked = true;
                continue;
            }
            if (mPendingIdleHandlers == null) {
                mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
            }
            mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
        }
        //只有第一次循環時,會運行idle handlers,執行完成後,重置pendingIdleHandlerCount爲0.
        for (int i = 0; i < pendingIdleHandlerCount; i++) {
            final IdleHandler idler = mPendingIdleHandlers[i];
            mPendingIdleHandlers[i] = null; //去掉handler的引用
            boolean keep = false;
            try {
                keep = idler.queueIdle();  //idle時執行的方法
            } catch (Throwable t) {
                Log.wtf(TAG, "IdleHandler threw exception", t);
            }
            if (!keep) {
                synchronized (this) {
                    mIdleHandlers.remove(idler);
                }
            }
        }
        //重置idle handler個數爲0,以保證不會再次重複運行
        pendingIdleHandlerCount = 0;
        //當調用一個空閒handler時,一個新message可以被分發,所以無需等待能夠直接查詢pending message.
        nextPollTimeoutMillis = 0;
    }
}

nativePollOnce(ptr, nextPollTimeoutMillis)是一個native方法,而且是阻塞操做。其中nextPollTimeoutMillis表明下一個消息到來前,還須要等待的時長;當nextPollTimeoutMillis = -1時,表示消息隊列中無消息,會一直等待下去。空閒後,每每會執行IdleHandler中的方法。當nativePollOnce()返回後,next()從mMessages中提取一個消息。nativePollOnce()在native作了大量的工做,想進一步瞭解可查看 Android消息機制2-Handler(native篇)

4.3 enqueueMessage

添加一條消息到消息隊列

boolean enqueueMessage(Message msg, long when) {
    // 每個Message必須有一個target
    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) {  //正在退出時,回收msg,加入到消息池
            msg.recycle();
            return false;
        }
        msg.markInUse();
        msg.when = when;
        Message p = mMessages;
        boolean needWake;
        if (p == null || when == 0 || when < p.when) {
            //p爲null(表明MessageQueue沒有消息) 或者msg的觸發時間是隊列中最先的, 則進入該該分支
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked; //當阻塞時須要喚醒
        } else {
            //將消息按時間順序插入到MessageQueue。通常地,不須要喚醒事件隊列,除非
            //消息隊頭存在barrier,而且同時Message是隊列中最先的異步消息。 
            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;
            prev.next = msg;
        }
        //消息沒有退出,咱們認爲此時mPtr != 0
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

MessageQueue是按照Message觸發時間的前後順序排列的,隊頭的消息是將要最先觸發的消息。當有消息須要加入消息隊列時,會從隊列頭開始遍歷,直到找到消息應該插入的合適位置,以保證全部消息的時間順序。

4.4 removeMessages

void removeMessages(Handler h, int what, Object object) {
    if (h == null) {
        return;
    }
    synchronized (this) {
        Message p = mMessages;
        //從消息隊列的頭部開始,移除全部符合條件的消息
        while (p != null && p.target == h && p.what == what
               && (object == null || p.obj == object)) {
            Message n = p.next;
            mMessages = n;
            p.recycleUnchecked();
            p = n;
        }
        //移除剩餘的符合要求的消息
        while (p != null) {
            Message n = p.next;
            if (n != null) {
                if (n.target == h && n.what == what
                    && (object == null || n.obj == object)) {
                    Message nn = n.next;
                    n.recycleUnchecked();
                    p.next = nn;
                    continue;
                }
            }
            p = n;
        }
    }
}

這個移除消息的方法,採用了兩個while循環,第一個循環是從隊頭開始,移除符合條件的消息,第二個循環是從頭部移除完連續的知足條件的消息以後,再從隊列後面繼續查詢是否有知足條件的消息須要被移除。

5、 Message

5.1 建立消息

每一個消息用Message表示,Message主要包含如下內容:

數據類型 成員變量 解釋
int what 消息類別
long when 消息觸發時間
int arg1 參數1
int arg2 參數2
Object obj 消息內容
Handler target 消息響應方
Runnable callback 回調方法

建立消息的過程,就是填充消息的上述內容的一項或多項。

5.2 消息池

在代碼中,可能常常看到recycle()方法,咋一看,多是在作虛擬機的gc()相關的工做,其實否則,這是用於把消息加入到消息池的做用。這樣的好處是,當消息池不爲空時,能夠直接從消息池中獲取Message對象,而不是直接建立,提升效率。

靜態變量sPool的數據類型爲Message,經過next成員變量,維護一個消息池;靜態變量MAX_POOL_SIZE表明消息池的可用大小;消息池的默認大小爲50。

消息池經常使用的操做方法是obtain()和recycle()。

obtain()

從消息池中獲取消息

public static Message obtain() {
    synchronized (sPoolSync) {
        if (sPool != null) {
            Message m = sPool;
            sPool = m.next;
            m.next = null; //從sPool中取出一個Message對象,並消息鏈表斷開
            m.flags = 0; // 清除in-use flag
            sPoolSize--; //消息池的可用大小進行減1操做
            return m;
        }
    }
    return new Message(); // 當消息池爲空時,直接建立Message對象
}

obtain(),從消息池取Message,都是把消息池表頭的Message取走,再把表頭指向next;

recycle()

把再也不使用的消息加入消息池

public void recycle() {
    if (isInUse()) { //判斷消息是否正在使用
        if (gCheckRecycle) { //Android 5.0之後的版本默認爲true,以前的版本默認爲false.
            throw new IllegalStateException("This message cannot be recycled because it is still in use.");
        }
        return;
    }
    recycleUnchecked(); 
}

//對於再也不使用的消息,加入到消息池
void recycleUnchecked() {
    //將消息標示位置爲IN_USE,並清空消息全部的參數。
    flags = FLAG_IN_USE;
    what = 0;
    arg1 = 0;
    arg2 = 0;
    obj = null;
    replyTo = null;
    sendingUid = -1;
    when = 0;
    target = null;
    callback = null;
    data = null;
    synchronized (sPoolSync) {
        if (sPoolSize < MAX_POOL_SIZE) { //當消息池沒有滿時,將Message對象加入消息池
            next = sPool;
            sPool = this;
            sPoolSize++; //消息池的可用大小進行加1操做
        }
    }
}

recycle(),將Message加入到消息池的過程,都是把Message加到鏈表的表頭;

總結

最後用一張圖,來表示整個消息機制

handler_java

圖解:

  • Handler經過sendMessage()發送Message到MessageQueue隊列;
  • Looper經過loop(),不斷提取出達到觸發條件的Message,並將Message交給target來處理;
  • 通過dispatchMessage()後,交回給Handler的handleMessage()來進行相應地處理。
  • 將Message加入MessageQueue時,處往管道寫入字符,能夠會喚醒loop線程;若是MessageQueue中沒有Message,並處於Idle狀態,則會執行IdelHandler接口中的方法,每每用於作一些清理性地工做。

消息分發的優先級:

  1. Message的回調方法:message.callback.run(),優先級最高;
  2. Handler的回調方法:Handler.mCallback.handleMessage(msg),優先級僅次於1;
  3. Handler的默認方法:Handler.handleMessage(msg),優先級最低。
相關文章
相關標籤/搜索