从源码分析Android Handler 消息机制

看了Danny老师的视频,从源码层学习了一下Handler消息机制中几个重要的类(Handler、MessageQueue、Looper和Message),从之前的一知半解到现在终于弄明白这个Handler消息机制到底是怎么进行工作的了,在这里记录下来,同时很感谢一张图看明白Android Handler 消息机制这篇博文,里面讲解的也很详细很清楚,本文的源码大都来自这篇博文中。有需要视频的小伙伴我可以分享给你。

概述

消息机制主要是指 Handler 的运行机制以及 Handler 同 MessageQueue 和 Looper配合工作的过程。 Handler 的主要作用是将某个任务切换到 Handler 所在的线程中去执行。

Looper

每个线程中最多只能有一个 Looper 对象,由 Looper 来管理此线程里的 MessageQueue (消息队列)。

可以通过 Looper.myLooper() 获取当前线程的 Looper 实例,通过 Looper.getMainLooper() 获取主(UI)线程的 Looper 实例。

Lopper 会以无限循环的形式去查找是否有新消息,如果有就处理消息,否则就一直等待着

Handler

你可以构造 Handler 对象来与 Looper 沟通,通过 push 发送新消息到 MessageQueue 里;或者通过 handleMessage 接收 Looper 从 MessageQueue 取出来消息。

MessageQueue

MessageQueue是一个消息队列,内部存储了一组消息,以队列的形式对外提供插入和 删除的工作,内部采用单链表的数据结构来存储消息列表。

ActivityThread

我们经常提到的主线程,也叫UI线程,它就是 ActivityThread,主线程启动会默认初始化一个 Looper 并创建 Handler。

一个线程中只有一个 Looper 实例,一个 MessageQueue 实例,可以有多个 Handler 实例。

ThreadLocal

一个线程内部的数据存储类,通过它可以在指定线程中存储数据,数据存储后,只有在指定线程中可以获取到存储的数据,对于其他线程来说无法获得数据。

对于 Handler 来说,它需要获取当前线程的 Looper ,而 Looper 的作用于就是线程并且不同的线程具有不同的 Looper ,通过 ThreadLocal 可以轻松实现线程中的存取。

ThreadLocal原理:不同线程访问同一个ThreadLoacl的get方法,ThreadLocal的get方法会从各自的线程中取出一个数组,然后再从数组中根据当前ThreadLocal的索引去查找对应的Value值。

源码分析

  1. Looper

Looper的构造方法中创建消息队列并保存当前线程;

private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);//创建消息队列
        mThread = Thread.currentThread();//保存当前线程
    }

prepare方法中将创建的Lopper放入到ThreadLocl中——sThreadLocal.set(new Looper(quitAllowed));(所以在使用Looper时先调prepare方法进行初始化);Looper也提供初始化主线程的Looper和获取主线程的LooperprepareMainLooper()、getMainLooper()Looperloop():在当前线程中开启轮询,首先从ThreadLocal中取出当前线程的looper对象——sThreadLocal.get()(没有调prepare方法会抛异常),再从looper对象中取出消息队列——final MessageQueue queue = looper.mQueue,进行死循环不断的取出消息queue.next(),取出的消息交给handler进行执行分发消息的操作——msg.target.dispatchMessage(msg);,消息已经分发后进行回收操作——msg.recycleUnchecked()

Looper源码

public final class Looper {

    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    //每个线程都会有一个ThreadLocal 用来保存 Looper对象(里面包含了主线程和 MessageQueue)

    private static Looper sMainLooper;  // 主线程的 Looper

    final MessageQueue mQueue;//保存消息队列
    final Thread mThread;//保存主线程

    public static void prepare() {//为当前线程创建 Looper
        prepare(true);
    }

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            //一个线程只能有一个 Looper, 否则抛出异常
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));//将创建的 Looper 放入 ThreadLocal
    }
    
    //初始化主线程的 Looper
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

    //获取主线程的 Looper
    public static Looper getMainLooper() {
        synchronized (Looper.class) {
            return sMainLooper;
        }
    }

    //在当前线程中开启轮询
    public static void loop() {
        final Looper me = myLooper();//从 ThreadLocal 中取出当前线程的 Looper 对象
        if (me == null) {
            //Looper 没有调用 Looper.prepare() 初始化,抛出异常
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;//从 Looper 对象中取出消息队列

        for (;;) {//死循环
            Message msg = queue.next(); // 不断的取出消息
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            ...             

            try {
                msg.target.dispatchMessage(msg);
                //取出消息的 target (也就是 Handler),执行分发消息的操作
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }

            ... 

            msg.recycleUnchecked();//消息已经分发,进行回收操作
        }
    }

    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();//从 ThreadLocal 中取出当前线程的 Looper 对象
    }

    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);//创建消息队列
        mThread = Thread.currentThread();//保存当前线程
    }

    public void quit() {
        mQueue.quit(false);//直接退出消息循环,不管是否还有消息
    }

    public void quitSafely() {
        mQueue.quit(true);//执行完所有的消息,退出消息循环
    }

    ...
}

  1. MessageQueue

主要说说消息是怎么加入消息队列和从消息队列中取出的,分别对应两个方法enqueueMessage() 和next()

消息在入队的时候,如果消息队列里面没有消息,或者消息的执行时间比里面的消息早,就把这条消息设置成第一条消息(一般不会出现这种情况,因为系统一定会有很多消息),如果消息队列里面有消息,找到消息队列里面的最后一条消息,把消息添加到最后。

取出消息时取出一条消息,消息队列往后移动一个,并将此消息比较为已使用。

加入消息的源码

boolean enqueueMessage(Message msg, long when) {
    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) {
            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();
        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;
}

取消息的源码

Message next() {
    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) {
                //处理异步的消息,暂不讨论
                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 {
                    //取出一条消息,消息队列往后移动一个
                    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 {
                // No more messages.
                nextPollTimeoutMillis = -1;
            }

            ...
    }
}

  1. Message 是什么
public final class Message implements Parcelable {
   
    public int what;//消息类型,标识消息的作用

    public int arg1;//整型参数1

    public int arg2;//整型参数2

    public Object obj;//复杂对象参数

    public Messenger replyTo;

    public int sendingUid = -1;

    /*package*/ static final int FLAG_IN_USE = 1 << 0;//标记消息已使用

    /** If set message is asynchronous */
    /*package*/ static final int FLAG_ASYNCHRONOUS = 1 << 1;//标记消息是否异步

    /** Flags to clear in the copyFrom method */
    /*package*/ static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE;

    /*package*/ int flags;//消息当前标记

    /*package*/ long when;//消息执行时间
    
    /*package*/ Bundle data;
    
    /*package*/ Handler target;//Handler 用于执行 handleMessage();
    
    /*package*/ Runnable callback;//消息是一个Runnable
    
    // sometimes we store linked lists of these things
    /*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;//消息最大数量

  //从整个Messge池中返回一个新的Message实例,允许我们在许多情况下避免分配新对象
    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. Handler 是怎么与 Looper 和 MessageQueue 一起搭配工作的

Handler的构造方法灵活方便,可以指定回调方法,也可以关联哪个线程的Looper,所以处理消息时根据创建Handler时参数及消息体的不同选择不同的方法执行:如果消息体是 Runnable 就执行run();如果创建Handler 时传入了 Callback,就执行 Callback 里面的逻辑;如果上述两种都没有实现,就执行 handleMessage 的逻辑。
发送消息Handler提供了4种发送消息的方法,分别是:

  1. 发送 Runnable 消息的post(Runnable r)
  2. 般更新 UI 时发送的消息,延时时间为0 sendMessage(Message msg)
  3. 发送延时消息 sendMessageDelayed(Message msg, long delayMillis)
  4. 发送指定发送时间的消息 sendMessageAtTime(Message msg, long uptimeMillis)

Handler 的源码:

public class Handler {
   
    public interface Callback {
        public boolean handleMessage(Message msg);
    }
    
    
    public void handleMessage(Message msg) {}
    
    /**
     * 消息处理
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {//如果消息体是 Runnable 就执行 run()
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                //如果创建 Handler 时传入了 Callback,就执行 Callback 里面的逻辑
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);//如果上述两种都没有实现,就执行 handleMessage 的逻辑
        }
    }

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

    public Handler(Callback callback) {
        this(callback, false);
    }

    public Handler(Looper looper) {//可以指定关联哪个线程的 Looper
        this(looper, null, false);
    }

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

    public Handler(boolean async) {
        this(null, async);
    }

    /**
     * 主线程调用的构造方法,主线程已经调用了 Looper.prepareMainLooper();
     *
     * @hide
     */
    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());
            }
        }

        mLooper = Looper.myLooper();//取出主线程的 Looper
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;//把 Handler 的 mQueue 指向 Looper 中的 mQueue
        mCallback = callback;
        mAsynchronous = async;
    }

    /**
     * 第二种构造方法,专门给子线程中创建 Handler 时使用的
     *
     * @hide
     */
    public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

    //发送 Runnable 消息
    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;
    }    
    
    //一般更新 UI 时发送的消息,延时时间为0
    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;
        //把字节传入 Message 中一起发送
        //Looper 中需要使用 Handler 来执行 dispatchMessage 方法
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

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

推荐阅读更多精彩内容