Handler源码简析

Handler由Message、MessageQueue、Looper 和Handler本身四大部分组成,我们这里分别看一下他们的实现原理和整体之间的关联

消息------Message

  • 消息的复用:Message.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();
        }
    
    1. 从Message 链表的头节点-----sPool中获取第一个消息,然后更新头节点
    2. 清空next信息
    3. 重置in-use标志位
    4. 更新消息池size
  • 消息的回收:Message.recyclerUnchecked()

    void recycleUnchecked() {
            // Mark the message as in use while it remains in the recycled object pool.
            // Clear out all other details.
            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) {//MAX_POOL_SIZE==50
                    next = sPool;
                    sPool = this;
                    sPoolSize++;
                }
            }
        }
    
    1. 标识消息used;
    2. 重置参数
    3. 加入到消息池

消息队列-------MessageQueue

​ 关键:使用链表保存MessageQueue;使用一个阻塞标记标示是否列表是否为空;记录next barrier的token

    .....
    Message mMessages;//链表头
    
    // Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout.
    private boolean mBlocked; //阻塞标示

    // The next barrier token.
    // Barriers are indicated by messages with a null target whose arg1 field carries the token.
    private int mNextBarrierToken; //分隔栏tekon
    .....
>barrier是一个target为null的Message,使用postSyncBarrier(SystemClock.uptimeMillis())方法加入MessageQueue中,它的arg1参数保存着token。
>
>当从MessageQueue中取出消息时,遇到barrier会停止取出barrier后面的同步消息,称为“同步消息分隔栏”。
  • 消息入队: enqueueMessage()

    boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {    //消息必须有一个处理它的目标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();    //消息入队后标记为--used
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            //添加消息到链表中
            if (p == null || when == 0 || when < p.when) {    
                //之前是空链表的时候读取消息会阻塞,新添加消息后唤醒
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                //插入消息到队列时,若消息队列阻塞、队列头是barrier并且当前消息是异步的时,需要唤醒队列
                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;
    }
    
  • 消息出队: 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 (;;) {
            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---是barrier,向后遍历找到第一个异步的消息
                    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;
                }
    
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    mBlocked = true;
                    continue;
                }
    
                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }
    
            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;
        }
    }
    

    ​ mIdleHandlers:监听MessageQueue阻塞的接口List,MessageQueue在阻塞时会遍历并调用idler.queueIdle()方法,该方法返回false则会在一次调用后移除该idler。

消息调度器----Looper

​ 属性:

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;

    /* If set, the looper will show a warning log if a message dispatch takes longer than time. */
    private long mSlowDispatchThresholdMs;
  • Looper初始化 Looper.prepare() :

​ 使用ThreadLocal保证每个线程各自拥有自己一个的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.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;
    
        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();    //标记这个消息被回收
        }
    }
    
  1. 无限循环取出Message,调用对应Handler的dispatchMessage()方法处理消息
  2. 标记消息被回收
  • Looper.quit()方法退出循环,其调用的是MessageQueue.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);
            }
        }
    
    1. 判断并更新标识位;

    2. removeAllMessagesLocked();

      立即把消息链表中的所有消息全部回收

 - 在停止后如果 Handler 还发送消息,会返回 false,表示入队失败
 - 这个方法是不安全的,一般建议使用下面那个
  1. removeAllFutureMessagesLocked();
 - 只会将还未执行的消息回收掉
 - 在调用之后添加进入的消息不会被处理,Handler.sendMessage 也会返回 false

Handler

  • 主要属性:
    final Looper mLooper;
    final MessageQueue mQueue;
    final Callback mCallback;   //构造Handler时可传的自定义处理方法
    final boolean mAsynchronous;
    IMessenger mMessenger;`
  • 消息处理方法:dispatchMessage(Message msg)
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
  1. 如果消息定义了callback,则调用handleCallback(msg),该方法是调用Message自身的callback方法;

    private static void handleCallback(Message message) {
        message.callback.run();
    }
    
  2. 消息的callback为null,调用mCallback的handleMessage(msg)方法,如果mCallback.handleMessage(msg)返回true,直接返回,不再调用handleMessage(msg);

    mCallback是构造Handler时可传的自定义处理方法,其内部定义了一个handleMessage(Message msg)方法;

  3. 最后调用handleMessage(msg)方法。

  • 发送消息

    • Message-----sendMessage(Message msg)
    • Runnable------post(Runnable r)

    post方法会将Runnable包装为Message,然后调用sendMessageDelayed()方法;

        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(Message msg)最后还是调用了消息队列的 enqueueMessage() 方法:

    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);
    }
    
  • 移除消息,调用mQueue.removeMessages方法,从队列中移除消息。

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

    参数r与Message.callback 比较

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容