Handler 源碼分析

 

1.概念

    Handler主要用於線程之間的通信,例如刷新主界面UI等,其通信流程圖如下:

Handler流程圖

在這個過程中,Handler即作爲發起者也作爲終結者,Message作爲基本信息載體,MessageQueue作爲消息體的集合,Looper將這三者串聯起來。

2.Message

2.1概念 message    

  官方解釋: 包含任意類型的對象和描述信息,可以被髮送給 Handler。(官方文檔:https://developer.android.com/reference/android/os/Message

2.2Message中關鍵變量

  1.What用於標識Message具體是做什麼的

  2.arg1、arg2用於存儲簡單的整型數據

  3.Bundle用於存儲複雜的數據

  4.跨進程通信綁定數據的 object(主要應用在Messenger跨進程通信https://blog.csdn.net/qq_16736297/article/details/85618729

5.與之關聯的Handler

6.另外就是一些標誌位

具體詳細的變量介紹如下:

 /**
     * 用來標識消息身份ID,接收消息方可以根據這個身份ID知道這個消息是做什麼的
     */
    public int what;

    /**
     *   如果你的消息要傳遞的數據是整型的,可以直接使用 arg1 和 arg2,而不需要使用構造一個 Bundle
     */
    public int arg1;
    public int arg2;

    /**
     * 一個任意類型的對象,在使用 Messenger 跨進程傳遞消息時,通常使用它傳遞給接收者
     * 在其他場景下我們一般使用 setData() 方法
     * 具體適用場景可以去訪問()
     */
    public Object obj;

    /**
     * 負責回覆消息的 Messenger,有的場景下(比如接受者、發送者模型)需要使用它
     */
    public Messenger replyTo;


    /**
     * 當前消息的標誌,只在被 Messenger 傳遞消息時使用,其他情況下都是 -1
     */
    public int sendingUid = -1;

    //標識當前消息是否在被使用
    //當一個消息入隊時這個標誌會被改變,在被重新獲取後重置
    //當一個消息已經在被使用時,二次入隊或者回收會報錯(這就是我前言中提到的錯誤原因)
    /*package*/static final int FLAG_IN_USE = 1 << 0;

    //標識當前 消息是否是異步的
    /*package*/static final int FLAG_ASYNCHRONOUS = 1 << 1;

    //在 copyFrom 方法中要清除的標誌
    /*package*/static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE;
    /*package*/int flags;

    /*package*/long when;

    //很關鍵的數據部分
    /*package*/Bundle data;

    //發送和處理消息關聯的 Handler
    /*package*/Handler target;

    //消息的回調
    /*package*/Runnable callback;

    //在有些場景下還會以鏈表的形式關聯後一個消息
    /*package*/Message next;

    //消息池
    private static final Object sPoolSync = new Object();
    /**
     * 回收消息鏈表
     */
    private static Message sPool;
    private static int sPoolSize = 0;

    private static final int MAX_POOL_SIZE = 50;

    /**
     * 是否回收標誌位
     */
    private static boolean gCheckRecycle = true;

2.3Message的獲取方式

  在官方文檔中Message中的有如下兩種:

    1.Message.obtain();

    2.Handler.obtainMessage();

需要注意的是Message的構造函數是public,但這裏並沒有去創建,而是選擇利用上面的這兩種方式從消息池中獲取,這樣做的原因是爲了節省內存。

另外這兩種獲取方式本質上並沒有什麼區別,從下面的源碼中可得知Handler.obtainMessage()還是調用了Message.obtain()

   /**
     * Returns a new {@link android.os.Message Message} from the global message pool. More 
     *efficient than
     *creating and allocating new instances. The retrieved message has its handler set to 
     *this instance (Message.target == this).
     *If you don't want that facility, just call Message.obtain() instead.
     */
    public final Message obtainMessage()
    {
        return Message.obtain(this);
    }

2.4Message.obtain()分析

  Message.obtain()在Message中有7個重載方法,此處只分析最複雜的一個,具體源碼如下:

 public static Message obtain(Handler h, int what,
            int arg1, int arg2, Object obj) {
        Message m = obtain();
        m.target = h;
        m.what = what;
        m.arg1 = arg1;
        m.arg2 = arg2;
        m.obj = obj;

        return m;
    }
     /**
      * Return a new Message instance from the global pool. Allows us to
      * avoid allocating new objects in many cases.
      */
    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();
    }

可以看到,這個方法會獲取前面提到的 private static Message sPool; ,如果 sPool 存在就從複用消息鏈表頭部取一個消息,然後重置它的標誌位;如果不存在複用消息鏈表就新建一個消息。

既然消息是從消息池中添加,那麼問題來了,消息是何時添加的呢?

2.5Message的回收利用

看下面的源碼

    void recycleUnchecked() {
        flags = FLAG_IN_USE;//信息標誌位flag,當信息被使用時,會被標誌爲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) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

recycleUnchecked()方法將Messageflag賦值爲FLAG_IN_USE,如果此時被標記過的Message再次被放進隊列,就會報錯this message is already in use。當然此時Message所承載的數據也被清除,然後將其放入消息回收池中,以待下次使用。

那麼問題又來了,recycleUnchecked()何時會被調用呢?扒開源碼,我們會發現此方法在MessageQueue 和 Looper中都有使用,

MessageQueue.removeMessages()的源碼如下:

 void removeMessages(Handler h, int what, Object object) {
        if (h == null) {
            return;
        }

        synchronized (this) {
            Message p = mMessages;

            // Remove all messages at front.
            while (p != null && p.target == h && p.what == what
                   && (object == null || p.obj == object)) {
                Message n = p.next;
                mMessages = n;
                p.recycleUnchecked();//在此處調用
                p = n;
            }

            // Remove all messages after front.
            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;
            }
        }
    }

Looper.loop()方法中有調用:

   /**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    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();

        // Allow overriding a threshold with a system prop. e.g.
        // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
        final int thresholdOverride =
                SystemProperties.getInt("log.looper."
                        + Process.myUid() + "."
                        + Thread.currentThread().getName()
                        + ".slow", 0);

        boolean slowDeliveryDetected = false;

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

            //此處省略部分代碼
            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();//在此處調用了recycleUnchecked()方法;
        }
    }

MessageQueue.removeMessages()Looper.loop()方法中可以看出,此處當消息被移除消息隊列時或者被Looper處理以後就會被標識爲FLAG_IN_USE,然後被放入消息回收池中,當我們再次使用時,會通過Message.obtain()到消息鏈表進行獲取。

那麼問題再次來了,什麼是消息回收池,所謂的消息池用於存放還未處理的消息

3.MessageQueue

  MessageQueue是一個管理Message的消息列表,由Handler爲其添加Message,由Looper進行取出。

3.1Message的主要關鍵變量

  // 隊列是否可以退出
    private final boolean mQuitAllowed;

    @SuppressWarnings("unused")
    private long mPtr; //底層使用的 code

    //消息鏈表的開頭
    Message mMessages;
    private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
    private SparseArray<FileDescriptorRecord> mFileDescriptorRecords;
    private IdleHandler[] mPendingIdleHandlers;
    private boolean mQuitting;
    //指出獲取下一個消息的方法 next() 是否阻塞
    private boolean mBlocked;

    // Barriers are indicated by messages with a null target whose arg1 field carries the 
       token.
    //後一個屏障的 token 
    private int mNextBarrierToken;

MessageQueue其實類似於LinkList列表,持有消息列表的一個節點

3.2MessageQueue的初始化

MessageQueue是不能直接被訪問,而是通過Looper.myQueue()方法進行獲取,源碼如下:

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

可以從MessageQueue的構造函數中看出,其構造函數傳入一個是否中途撤出的標誌位,有native層初始化。

當我們獲取MessageQueue之後,開始向其添加Message

3.3消息入隊的過程

Message入隊通過enqueueMessage()方法,讓我們來一起扒拉扒拉這個方法的源碼:

boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {//此處必須給Message設置一個目標,即與他相關聯的Handler
            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();//Message入隊以後就會被標記爲被使用
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            //添加消息到鏈表中
            if (p == null || when == 0 || when < p.when) {
                //空鏈表的時候讀取信息會被堵塞,新添加消息後喚醒
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                //插入消息到隊列時,只有在隊列頭部有個屏障並且當前消息是異步的時才需要喚醒隊列
                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;
    }

在此方法中,有一步檢查消息是否被使用,是的話會報錯,具體如下:

if (msg.isInUse()) {    
        throw new IllegalStateException(msg + " This message is already in use.");
}

由上可知當我們在Handler.sendMessage()時,必須Message.obtain()或者new Message()對象。當然,如果已退出隊列再次添加也會報錯。說完消息進隊,下面該講消息如何出隊

3.4消息出隊

Message的出隊是有MessageQueue.next()方法進行,接下來我們開始進行擼碼行動

Message next() {
        // 如果消息的Looper退出,就退出這個方法
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        //循環,有消息就返回,沒有消息就阻塞
        for (;;) {
           //如果有過段時間在需要要處理的消息,就先調用Binder的這個方法
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                // 獲取下一個消息
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;//當前鏈表的頭節點
                if (msg != null && msg.target == null) {
                    //如果消息沒有 target,那它就是一個屏障,需要一直往後遍歷找到第一個異 
                    //步的消息
                      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;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();//標記這個消息在被使用
                        return msg;
                    }
                } else {
                    // 消息鏈表裏沒有可以處理的消息了
                    nextPollTimeoutMillis = -1;
                }

               //如果收到退出的消息,並且所有等待處理的消息都處理完時,調用 Native 方法銷燬隊列
                if (mQuitting) {
                    dispose();
                    return null;
                }

                //有消息等待過段時間執行時,pendingIdleHandlerCount 增加
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }

                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            // Run the idle handlers.
            // We only ever reach this code block during the first iteration.
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the handler

                boolean keep = false;
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            // Reset the idle handler count to 0 so we do not run them again.
            pendingIdleHandlerCount = 0;

            // While calling an idle handler, a new message could have been delivered
            // so go back and look again for a pending message without waiting.
            nextPollTimeoutMillis = 0;
        }
    }

由上可知,在next()方法裏有一個循環,循環的目的是找到消息鏈表中一個可以處理的,target不爲空的並且可執行的事件不再未來的消息就返回,否則繼續找,當然如果無任何消息或者未來一段時間在處理,這時會將mBlock這個標量置爲true,然後在下一個Message判斷進隊時就會判斷這個Message的位置,如果在隊首就會調用 nativeWake() 方法喚醒線程!

在有關堵塞的問題上,我們發現MessageQueue.java中有一個IdleHandler,這個方法是幹嘛用的呢

public static interface IdleHandler {
        /**
         * 1.消息隊列沒有消息時,就會調用這方法,阻塞等待消息進入
         * 2.返回true的話表示喚醒堵塞線程,false表示移除
         * 3.如果消息隊列中有消息等待在將來執行,也會調用這個方法      
         */
        boolean queueIdle();
    }

根據註釋和源碼可知,IdleHandler是一個線程阻塞時回調的接口。當然在MessageQueue提供監聽阻塞回調的註冊和移除接口:

public void addIdleHandler(@NonNull IdleHandler handler) {
        if (handler == null) {
            throw new NullPointerException("Can't add a null IdleHandler");
        }
        synchronized (this) {
            mIdleHandlers.add(handler);
        }
}

public void removeIdleHandler(@NonNull IdleHandler handler) {
        synchronized (this) {
            mIdleHandlers.remove(handler);
        }
    }

但消息隊列被阻塞時,會回調監聽阻塞的觀察者,告訴他們:“我有空了,趕緊嗨起來!!!”

4.Looper

4.1概念:Looper 是用於運行一個線程中的消息的類。

4.2Looper的關鍵屬性

// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper;  // 主線程中的 Looepr

final MessageQueue mQueue;    //與之管理的消息隊列
final Thread mThread;    //所在的線程

private Printer mLogging;
private long mTraceTag;

4.3線程相關的ThreadLocal

在線程如何通過Looper.prepare()創建一個Looper

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

通過上述代碼可知,每個線程只有一個Looper,而創建Looper都需要通過ThreadLocal,那ThreadLocal又是何方神聖呢,我們來扒拉扒拉其中主要的源碼:

 public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }
  
 public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

從上面的源碼set和get中有一個ThreadLocalMap,這個ThreadLocalMap裏持有一個Entry,擼下源碼:

 static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }

從上面的源碼,可以看出ThreadLocal先通過當前線程獲取一個ThreadLocalMap,然後在ThreadLocalMap中保存ThreadLocal和數據的關聯。ThreadLocalMap類似於Map

4.4無限循環調度

通常在線程創建Looper對象以後,就可以調度Looper.loop()方法了,具體的源碼如下:

/**
 * Run the message queue in this thread. Be sure to call
 * {@link #quit()} to end the loop.
 */
public static void loop() {
    final Looper me = myLooper();
    if (me == null) {    //當前線程必須創建 Looper 纔可以執行
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;

    //底層對 IPC 標識的處理,不用關心 
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();

    for (;;) {    //無限循環模式
        Message msg = queue.next(); //從消息隊列中讀取消息,可能會阻塞
        if (msg == null) {    //當消息隊列中沒有消息時就會返回,不過這隻發生在 queue 退出的時候
            return;
        }

        //...
        try {
            msg.target.dispatchMessage(msg);    //調用消息關聯的 Handler 處理消息
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        //...
        msg.recycleUnchecked();    //標記這個消息被回收
    }
}

pasted:https://blog.csdn.net/u011240877/article/details/72892321 

由上可知,Looper.loop(),通過MessageQueue.next()取出信息,如果沒有消息的話的會阻塞,直到有新的消息進入或者消息隊列退出。

當拿到消息後,調用消息相關聯的Handler處理。

既然能開啓循環那麼如何終止循環呢

4.5退出循環

  在源碼註釋中就提到,當開啓循環後,不要忘記調用quit()方法終止循環。

退出循環有兩個方法在Looper裏,quit和quitSafely的源碼

public void quit() {
    mQueue.quit(false);
}

public void quitSafely() {
    mQueue.quit(true);
}

而兩種方式裏調用的最終都是MessageQueue.quit(boolean),具體如下:

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;
}
plaste:https://blog.csdn.net/u011240877/article/details/72892321 

從上述源碼可以看出,這兩種方式的區別

1.quit():立即把消息鏈表中的消息都回收掉

a.在停止後,如果Handler還發送消息,就會返回false,入隊失敗

b.由此也可得出此方法並不安全

2.quitSafely()

a.只會將未執行的消息回收掉

b.在調用之後添加的消息,不會被處理,Handler.sendMessage()也會返回false

當消息隊列表標記退出狀態時,他的next()方法會返回null,於是Looper.loop()循環就結束了

5.Handler

在介紹完LooperMessageQueueMessage之後,接着該主角登場了,作爲最終的發起者和結束者,Handler又是何方神聖呢?

Handler所做的就是切換線程:

a.在子線程中將Message或者Runable發送到發送到消息隊列MessageQueue中。

b.然後等待Looper調度這個Message之後,再度召喚Handler來處理。

c.這時消息已經在創建Handler的線程了

接下來我們逐步分析Handler是如何實現線程間的切換的

5.1Handler的關鍵屬性變量

final Looper mLooper;
final MessageQueue mQueue;
final Callback mCallback;
final boolean mAsynchronous;
IMessenger mMessenger;

由上不難發現,Handler屬性十分簡單,其中Callback可以作爲構造函數的參數創建Handler

public Handler(Callback callback) {
    this(callback, false);
}
public interface Callback {
    public boolean handleMessage(Message msg);
}
paste:https://blog.csdn.net/u011240877/article/details/72892321 

平時我們在使用Handler中,一般都是創建Handler的子類,然後重寫其handleMessage()方法,現在可以通過Callback()構建Handler,如下:

Handler mHandler = new Handler(new Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {
        //這裏處理消息
        return false;
    }
});

paste:https://blog.csdn.net/u011240877/article/details/72892321 

但是這種方式容易造成內存泄露,因爲是匿名內部類,持有外部引用。

OK,瞭解Handler的如何創建之後,那Handler是如何發送信息

5.2發送信息

Handler發送的數據類型有兩種:Message和Runnable

發送方式有兩種post和send,post發送的是Runnable,而send發送的是Message,但Runnable最終也會轉成Message

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

有上述源碼可知,我們直接去扒拉sendMessage源碼

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);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

由上面的源代碼中可知,handler在發送消息後最終調用了MessageQueue.enqueueMessage()方法。

而我們最常使用的Handler.sendMessagedDelayed()發送延遲消息後,其實在最後入隊後指定這個Message的msg.when(),在MessageQueue.next()方法中,會對msg.when>now的消息作出延遲處理,其具體實現在Native層。

當消息入隊後,由Looper啓動後,循環取出消息,交給target.dispatchMessage(),Handler.dispatchMessage()來處理消息,其具體的源碼如下:

/**
 * Handle system messages here.
 */
public void dispatchMessage(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 void handleMessage(Message msg) {
}

有上述源碼可知,Handler在處理消息時有三種情況:

a.當msg.callback不爲空時

    a.這在使用post方式發送消息時,會發生

   b.直接調用Runnable的run方法

b.當mCallback不爲空時

  a.這在之前的源碼分析中我們曾提到,當Handler.callBack爲參數構造Handler時調用

  b.那就調用構造函數裏傳入handlerMessage()方法

  c.如果返回true就不行再往下執行。

c.最後調用handleMessage()這是一個空實現,需要我們在Handler子類裏面重寫。

5.3移除信息

由於發送時可以發送 Callback 和 Message,所以取消也有兩種:

  1. removeCallbacks()
  2. removeMessages()

看一下源碼發現調用的其實就是消息隊列的出隊方法:

public final void removeCallbacks(Runnable r){
    mQueue.removeMessages(this, r, null);
}
public final void removeMessages(int what) {
    mQueue.removeMessages(this, what, null);
}

至此結束:

參考:

1.Android藝術探索

2.https://blog.csdn.net/u011240877/article/details/72892321

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