Handler、MessageQueue、Looper底层原理梳理(详解)

目录

一、概述

二、Message以及MessageQueue

2.1 Message

2.1.1 Message的构造函数 obtain()

2.1.2 回收Message对象 recycleUnchecked()

2.1.3 给目标handler快速发消息 sendToTarget()

2.2 MessageQueue

2.2.1 主线程MessageQueue的创建

2.2.2 Native层MessageQueue的监听机制

2.2.3 将消息插入到队列 enqueueMessage

2.2.4 从队列中取出消息  next()

小结

2.2.5 Nativce层MessageQueue的唤醒机制 nativePollOnce

2.2.6 MessageQueue中的dump方法和其他常见方法

三、Handler

3.1 Handler中的发送消息

3.1.1 post()

3.1.2 postDelayed()

3.1.3 sendMessage()

3.1.4 sendEmptyMessage()

3.1.5  sendMessageAtFrontOfQueue()

3.2 Handler中的处理消息

3.2.1 dispatchMessage()

3.2.2 handleMessage()

3.2.3 Handler中的其他方法

3.2.3.1 createAsync()

3.2.3.2 getMessageName(Message message)

3.2.3.3 dump(Printer,String)

四、Looper

 4.1 Looper与Handler的绑定

 4.2 Looper的启动与退出

4.2.1 loop();

4.2.2 loopOnce()

4.2.3 quit()/quitSafely()

结语


一、概述

在安卓中,消息处理机制主要由Handler、MessageQueue、Looper构成,主要用于线程间的通信。

在Android中,为了避免阻塞主线程(UI)线程导致界面卡顿、耗时操作(如网络请求,数据库读写)需要在子线程执行。但更新UI只能在主线程进行,这时候就需要借助消息处理机制把任务从子线程传递到主线程。该机制的核心工作流程是:子线程把消息发送到消息队列,主线程从消息队列取出消息并处理。

MessageQueue
定义:MessageQueue 即消息队列,本质是一个基于单链表的数据结构,用于存储 Message 对象。Message 是一个封装了消息数据和处理信息的类,它包含了消息的标识、携带的数据等内容。
作用:它负责管理和维护消息,遵循先进先出(FIFO)原则。当有新的消息被发送过来时,会被添加到队列的尾部;而 Looper 会从队列的头部取出消息进行处理。


Looper
定义:Looper 负责循环从 MessageQueue 中取出消息,并将其分发给对应的 Handler 进行处理。每个线程只能有一个 Looper 对象,并且可以通过 Looper.prepare() 方法为当前线程创建 Looper,通过 Looper.loop() 方法启动消息循环。
作用:不断地从 MessageQueue 中取出消息,然后将消息传递给相应的 Handler 进行处理。只要 MessageQueue 中有消息,Looper 就会持续循环处理,直到调用 Looper.quit() 方法退出循环。


Handler
定义:Handler 是消息的发送者和处理者,主要用于发送消息到 MessageQueue 以及处理从 MessageQueue 中取出的消息。可以在创建 Handler 时指定关联的 Looper,如果不指定,则默认关联当前线程的 Looper。
作用:
发送消息:提供了多种发送消息的方法,如 sendMessage()post() 等。通过这些方法,可以将消息发送到 MessageQueue 中等待处理。
处理消息:通过重写 handleMessage() 方法来处理从 MessageQueue 中取出的消息。当 Looper 从 MessageQueue 中取出消息并分发给 Handler 时,Handler 会调用 handleMessage() 方法进行具体的处理。

简易的三者关系图

MessageQueue:保存了许多Message

Looper:不断获取MessageQueue中的Message给Handler处理

Handler:对传入的Object进行处理

二、Message以及MessageQueue

2.1 Message

frameworks/base/core/java/android/os/Message.java

2.1.1 Message的构造函数 obtain()

    /**
     * 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;    //返回Messgae实例
            }
        }
        return new Message();     //池空的时候创建
    }

从Message的构造函数中我们可以看出Message通过静态方法obtain()实现对象复用,内部维护了一个静态链表池(最大容量MAX_POOL_SIZE=50),以sPool为头节点。当消息处理完毕时会通过recycleUnchecked()方法回收对象,清空字段并插入链表头部,等待复用。

这样的优势是提升性能,减少GC压力。

静态链表池:

     sPool(静态头指针)

               ⬇

        MessageA -> MessageB->MessageC ->null

sPool永远指向池子的头部,并且每个Message实例有自己的next指针指向下一个。链表头部是静态变量,所有对象共享。每次获得和回收Message对象都是对这个静态链表进行操作。使用静态链表而不是静态数组是因为静态链表可以灵活伸缩,不用预先分配内存。

我们在使用的过程中也要避免直接构造(频繁的创建新对象会造成内存抖动),优先使用obtain()方法或者通过Handler的封装方法handler.obtainMessage(what)直接调用。

除了Message的构造函数外,内部为了简化消息初始化还提供了多个重载方法,并且内部都调用obtain()复用对象。

 /**
     * Same as {@link #obtain()}, but copies the values of an existing
     * message (including its target) into the new one.
     * @param orig Original message to copy.
     * @return A Message object from the global pool.
     */
//这个方法的功能是复制已有消息的所有字段
//适用场景:需要发送与原消息内容完全相同(重试机制)或者稍有修改的新消息时候
    public static Message obtain(Message orig) {
        Message m = obtain();
        m.what = orig.what;    //what是int类型的 用于区分不同类型的消息
        m.arg1 = orig.arg1;    //arg1和arg2轻量级数据适合传递整数(进度值)
        m.arg2 = orig.arg2;
        m.obj = orig.obj;      //传递对象,跨进程时需实现Parcelable
        m.replyTo = orig.replyTo;    //跨进程通信时用于指定回复接收方 类型为Messenger
        m.sendingUid = orig.sendingUid;    //发送方身份标识 基于发送方的Linux UID用于跨进程校验,接收方会用Binder.getCallingUid()方法进行校验
        m.workSourceUid = orig.workSourceUid;  //实际触发操作的UID 比如前台应用委托后台进行操作
        if (orig.data != null) {
            m.data = new Bundle(orig.data); //Bundle对象
        }
        m.target = orig.target;   //Handler类型 用于指定处理此消息的Handler
        m.callback = orig.callback;  //Runnable类型,消息被处理时执行的异步任务

        return m;
    }

    /**
     * Same as {@link #obtain()}, but sets the value for the <em>target</em> member on the Message returned.
     * @param h  Handler to assign to the returned Message object's <em>target</em> member.
     * @return A Message object from the global pool.
     */
//用于给Message绑定Handler
//适用场景:消息内容如what obj不确定需要后续动态设置
    public static Message obtain(Handler h) {
        Message m = obtain();
        m.target = h;

        return m;
    }

    /**
     * Same as {@link #obtain(Handler)}, but assigns a callback Runnable on
     * the Message that is returned.
     * @param h  Handler to assign to the returned Message object's <em>target</em> member.
     * @param callback Runnable that will execute when the message is handled.
     * @return A Message object from the global pool.
     */
//设置target和callback
//适用场景:不需要处理what逻辑 直接调用回调
//比如 Message.obtain(handler, () -> {
//textView.setText("Task Complete"); 
//});
    public static Message obtain(Handler h, Runnable callback) {
        Message m = obtain();
        m.target = h;
        m.callback = callback;

        return m;
    }

    /**
     * Same as {@link #obtain()}, but sets the values for both <em>target</em> and
     * <em>what</em> members on the Message.
     * @param h  Value to assign to the <em>target</em> member.
     * @param what  Value to assign to the <em>what</em> member.
     * @return A Message object from the global pool.
     */
//设置target和消息标识what
//适用场景:发送仅标识类型,无附加数据的轻量消息比如通知状态变更
    public static Message obtain(Handler h, int what) {
        Message m = obtain();
        m.target = h;
        m.what = what;

        return m;
    }

    /**
     * Same as {@link #obtain()}, but sets the values of the <em>target</em>, <em>what</em>, and <em>obj</em>
     * members.
     * @param h  The <em>target</em> value to set.
     * @param what  The <em>what</em> value to set.
     * @param obj  The <em>object</em> method to set.
     * @return  A Message object from the global pool.
     */
//适用场景:传递单个复杂对象
    public static Message obtain(Handler h, int what, Object obj) {
        Message m = obtain();
        m.target = h;
        m.what = what;
        m.obj = obj;

        return m;
    }

    /**
     * Same as {@link #obtain()}, but sets the values of the <em>target</em>, <em>what</em>,
     * <em>arg1</em>, and <em>arg2</em> members.
     *
     * @param h  The <em>target</em> value to set.
     * @param what  The <em>what</em> value to set.
     * @param arg1  The <em>arg1</em> value to set.
     * @param arg2  The <em>arg2</em> value to set.
     * @return  A Message object from the global pool.
     */
//适用场景:传递多个整数值 不需要传递对象比如说更新下载进度
    public static Message obtain(Handler h, int what, int arg1, int arg2) {
        Message m = obtain();
        m.target = h;
        m.what = what;
        m.arg1 = arg1;
        m.arg2 = arg2;

        return m;
    }

    /**
     * Same as {@link #obtain()}, but sets the values of the <em>target</em>, <em>what</em>,
     * <em>arg1</em>, <em>arg2</em>, and <em>obj</em> members.
     *
     * @param h  The <em>target</em> value to set.
     * @param what  The <em>what</em> value to set.
     * @param arg1  The <em>arg1</em> value to set.
     * @param arg2  The <em>arg2</em> value to set.
     * @param obj  The <em>obj</em> value to set.
     * @return  A Message object from the global pool.
     */
//完全体形态 适用场景:传递整型参数和对象
    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;
    }

2.1.2 回收Message对象 recycleUnchecked()

作用:将当前Message对象回收到静态对象池,方便下次复用。

/**
     * Recycles a Message that may be in-use.
     * Used internally by the MessageQueue and Looper when disposing of queued Messages.
     */
    @UnsupportedAppUsage
    void recycleUnchecked() {
        // Mark the message as in use while it remains in the recycled object pool.
        // Clear out all other details.
//将Message的flag标记为正在使用 然后清空所有数据
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = UID_NONE;
        workSourceUid = UID_NONE;
        when = 0;
        target = null;
        callback = null;
        data = null;
 //回收到池中
        synchronized (sPoolSync) {   //加锁 保证多线程安全
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;   //指向池头
                sPool = this;   //池头指向当前message
                sPoolSize++;   //池大小+1
            }
        }
    }

2.1.3 给目标handler快速发消息 sendToTarget()

本质上和handler.sendMessage(msg)方法是一样的,用于快速发送消息。这里我们发现Message不指定某个handler也能发送,说明其内部其实是有一个Handler的。

从Message的成员变量中我们可以看到Message持有了一个Handler变量。这个Handler不从外部引入的原因是其内部需要初始化变量,指定目标handler等,如果每次都需要从外部传入还需要再维护一份Messgae和Handler的绑定关系,不利于系统的回收,池化。

    /**
     * Sends this Message to the Handler specified by {@link #getTarget}.
     * Throws a null pointer exception if this field has not been set.
     */
    public void sendToTarget() {
        target.sendMessage(this);
    }

2.2 MessageQueue

MessageQueue本质上是维护一个(已经按照执行时间排好序)的Message链表队列。负责消息的存储,排序,插入,删除以及出队。

MessageQueue不能直接new,由Looper通过Looper.prepare()进行创建,每一个Looper维护一个MessageQueue。

MessageQueue的主要成员:

  • Message mMessages;队列头指针,单链表结构
  • Message mLast;          队尾指针
  • ArrayList<IdleHandler>  空闲状态下的回调处理
  • boolean mQuitting          标志队列是否退出
  • private final boolean mQuitAllowed    是否允许退出
  • int mAsyncMessageCount;                 异步消息数量

2.2.1 主线程MessageQueue的创建

主线程MessageQueue的创建与子线程不同,是由系统进行创建的,创建的过程为​ActivityThread.main()→ Looper.prepareMainLooper()→ Looper

frameworks/base/core/java/android/app/ActivityThread.java

当应用进程启动时,系统会调用ActivityThread.main()(主线程入口方法)

 public static void main(String[] args) {
      Looper.prepareMainLooper(); //初始化主线程Looper和MessgaeQueue
      ActivityThread thread = new ActivityThread();
      Looper.loop();  //启动looper循环
}

Looper.prepareMainLooper()

frameworks/base/core/java/android/os/Looper.java

    /**
     * Initialize the current thread as a looper, marking it as an
     * application's main looper. See also: {@link #prepare()}
     *
     * @deprecated The main looper for your application is created by the Android environment,
     *   so you should never need to call this function yourself.
     */
    @Deprecated
    public static void prepareMainLooper() {
        prepare(false);  //创建主线程的Looper实例  这里传false表示禁止退出
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

prepare()

frameworks/base/core/java/android/os/Looper.java

   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并存储到ThreadLocal
    }


    private Looper(boolean quitAllowed) {
        //可以看到这里创建了一个新的MessageQueue 
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

MessageQueue的内部调用了JNI方法。

    MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();
//这里是一个JNI方法 功能是初始化Native层的消息队列
    }

2.2.2 Native层MessageQueue的监听机制

frameworks/base/core/jni/android_os_MessageQueue.cpp

static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
    NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
    if (!nativeMessageQueue) {
        jniThrowRuntimeException(env, "Unable to allocate native queue");
        return 0;
    }

    nativeMessageQueue->incStrong(env);//增加引用计数
    return reinterpret_cast<jlong>(nativeMessageQueue);//返回指针地址给Java层
}


NativeMessageQueue::NativeMessageQueue() :
        mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
//获取当前线程的Native Looper
    mLooper = Looper::getForThread();
    if (mLooper == NULL) {
//没有的话就创建一个 可以看到方法和Java层的非常相似
        mLooper = new Looper(false);
        Looper::setForThread(mLooper); //将当前线程绑定到一个Looper
    }
}

这里为什么不是跟上层的Looper一样主线程false 其他线程true区分开来呢?

这是Native层的退出唤醒流程是由Java层来决定的,这种写法是一种安全的写法,为了保证所有线程的NativeLooper一致,防止Native层的Looper因为生命周期的变化而意外中断,真正是否允许退出消息循环,是在 Java 层 MessageQueue 对 quit 的包裹。

system/core/libutils/Looper.cpp

Looper::Looper(bool allowNonCallbacks)
    : mAllowNonCallbacks(allowNonCallbacks),
      mSendingMessage(false),
      mPolling(false),
      mEpollRebuildRequired(false),
      mNextRequestSeq(WAKE_EVENT_FD_SEQ + 1),
      mResponseIndex(0),
      mNextMessageUptime(LLONG_MAX) {
//创建事件描述符(唤醒机制的核心)
    mWakeEventFd.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));
    LOG_ALWAYS_FATAL_IF(mWakeEventFd.get() < 0, "Could not make wake event fd: %s", strerror(errno));

    AutoMutex _l(mLock);
//初始化epoll监听机制
    rebuildEpollLocked();
}

mWakeEventFd是事件驱动唤醒的核心,通过Linux创建事件文件描述符,用于线程间异步唤醒。

  • EFD_NONBLOCK​:设为非阻塞模式,避免 read()/write()阻塞线程。

  • EFD_CLOEXEC​:进程执行 exec()时自动关闭该描述符,防止泄漏

MessageQueue在处理紧急消息的时候,会去根据Looper的阻塞状态来决定是否唤醒Looper,这里的唤醒原理是:Java层的MessageQueue调用nativeWake()时,会向mWakeEventFd写入数据,触发epoll的可读事件,唤醒阻塞在epoll_wait()的线程。

system/core/libutils/Looper.cpp

rebuildEpollLocked()

void Looper::rebuildEpollLocked() {

    // Allocate the new epoll instance and register the WakeEventFd.
//这里创建新的epoll实例   EPOLL_CLOEXEC确保子进程执行exec()时自动关闭描述符,防止资源泄露
    mEpollFd.reset(epoll_create1(EPOLL_CLOEXEC));
    LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));
//注册唤醒事件描述符 
//EPOLLIN为监听事件 监听到上层唤醒就会唤醒线程
//WAKE_EVENT_FD_SEQ为关联序列号,标识事件来源
    epoll_event wakeEvent = createEpollEvent(EPOLLIN, WAKE_EVENT_FD_SEQ);
    int result = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mWakeEventFd.get(), &wakeEvent);
//将唤醒事件(mWakeEventFd)添加到epoll实例
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake event fd to epoll instance: %s",
                        strerror(errno));

//重新注册所有待监听的文件描述符,将request队列里的事件也添加到epoll实例
    for (const auto& [seq, request] : mRequests) {
        epoll_event eventItem = createEpollEvent(request.getEpollEvents(), seq);

        int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, request.fd, &eventItem);
        if (epollResult < 0) {
            ALOGE("Error adding epoll events for fd %d while rebuilding epoll set: %s",
                  request.fd, strerror(errno));
        }
    }
}

这里的唤醒事件mWakeEventFd是用来唤醒队列中正处于epoll_wait的线程。是上层调用wake方法后触发的。

request队列里的事件来自于对特定fd的监听,一开始以为这里也跟上层的MessageQueue有关系,但实际是两种机制。典型的应用场景是Looper对InputReader里fd的监听,这是两种独立的监听机制。

使用epoll+eventfd的监听机制的好处是:

1、除了满足Java层监听事件的需求,也满足了Native层。

2、epoll的优势是可以多路复用,单线程监听成百上千个IO事件

3、eventfd是轻量级的计数描述符,写入数据即可触发唤醒,替代了复杂的管道和信号机制。

4、当消息队列为空时,线程会在epoll_wait()处阻塞,释放了CPU资源。

5、Linux内核自动回收关闭的文件描述符资源,避免内存泄漏

2.2.3 将消息插入到队列 enqueueMessage

 boolean enqueueMessage(Message msg, long when) {
//一开始对几种异常状态进行判断  1、handler为空 2、message正在使用 3、message正在退出
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }

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

            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) {
                // New head, wake up the event queue if blocked.
        // 1. 队列为空                → 直接作为队头
        // 2. 入队消息的when==0      → 要马上执行,比队列头还早,插到头部
        // 3. 入队消息when < 队头    → 明显优先级最高,插到头部
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;  //这个blocked值表示Looper是否阻塞,阻塞的话来了个紧急的消息需要立马处理就要唤醒looper,没阻塞的话就不需要唤醒。
                if (p == null) {
                    mLast = mMessages; //队头为空说明这个消息也是队尾 让mLast指向同一个节点
                }
            } else {
         //已有队头消息,找合适位置从头遍历链表
                // Message is to be inserted at tail or middle of 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();

                // For readability, we split this portion of the function into two blocks based on
                // whether tail tracking is enabled. This has a minor implication for the case
                // where tail tracking is disabled. See the comment below.
                if (Flags.messageQueueTailTracking()) { 
                //启用尾插功能 可以优化查询速度
                    if (when >= mLast.when) {
                    //插入到链尾
                        needWake = needWake && mAsyncMessageCount == 0;
                        msg.next = null;
                        mLast.next = msg;
                        mLast = msg;
                    } else {
                        // Inserted within the middle of the queue.
                       //插入链表中间
                        Message prev;
                        for (;;) {
                            prev = p;
                            p = p.next;
                            if (p == null || when < p.when) {
                                break;
                            }
                            if (needWake && p.isAsynchronous()) {
                                needWake = false;
                            }
                        }
                        if (p == null) {
                            /* Inserting at tail of queue */
                            mLast = msg;
                        }
                        msg.next = p; // invariant: p == prev.next
                        prev.next = msg;
                    }
                } else {
         //关闭尾插优化  有时候在低内存,避免内存泄漏的场景会关闭
                    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;

                    /*
                     * If this block is executing then we have a build without tail tracking -
                     * specifically: Flags.messageQueueTailTracking() == false. This is determined
                     * at build time so the flag won't change on us during runtime.
                     *
                     * Since we don't want to pepper the code with extra checks, we only check
                     * for tail tracking when we might use mLast. Otherwise, we continue to update
                     * mLast as the tail of the list.
                     *
                     * In this case however we are not maintaining mLast correctly. Since we never
                     * use it, this is fine. However, we run the risk of leaking a reference.
                     * So set mLast to null in this case to avoid any Message leaks. The other
                     * sites will never use the value so we are safe against null pointer derefs.
                     */
        //这段注释概括就是 既然这时候用不到mLast 为了安全起见就不要指着它,避免无用持有 产生GC
                    mLast = null;
                }
            }

            if (msg.isAsynchronous()) {
                mAsyncMessageCount++;   //这里是统计异步消息的数量
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr); //是否需要唤醒Looper线程
            }
        }
        return true;
    }

2.2.4 从队列中取出消息  next()

  @UnsupportedAppUsage
    Message next() {
        // Return here if the message loop has already quit and been disposed.
        // This can happen if the application tries to restart a looper after quit
        // which is not supported.
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;   //这个值是控制native层排队/等待新消息的超时时长
        for (;;) {
           //死循环 没退出就持续等待处理消息
            if (nextPollTimeoutMillis != 0) {
//没超时的话就先清理一些binder缓冲
                Binder.flushPendingCommands();
            }
            nativePollOnce(ptr, nextPollTimeoutMillis);
//通过native方法阻塞/唤醒 epoll上的线程
            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null; //每次调用msg这个指向都会置空
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
// Stalled by a barrier.  Find the next asynchronous message in the queue.
//注释里的barrier是target为null的特殊message,如果是这样的message会起到一个阻断消息分发的作用,不会被任何handler处理
//这样设计的原因是为了让更高优先级的异步消息 如input Vsync动画刷新等可以插队
                    do {
                        prevMsg = msg; //让pervMsg指向barrier的msg
                        msg = msg.next; //msg向后推进,寻找第一个异步消息
                    } 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;
//说明处理的是中间的msg,那么把中间的拆除掉的话,前面的prevMsg就要指向拆除掉的后一项
                            if (prevMsg.next == null) {
                            //后一项也为空,说明他就已经是尾巴了
                                mLast = prevMsg;
                            }
                        //这里是调整prevMsg指向的位置
                        } else {
//prevMsg为null,说明要处理的是队头,那么就直接把头节点后移
//这里为什么不需要prevMsg了呢,因为在安卓消息中,只有遇到barrier才会遍历前面的同步消息找到后面的异步消息,这时候摘除的消息在中间,所以用到prevMsg。像处理队头消息这种prevMsg一直都是空的,不用去管它。那如果是先在处理中间消息,下一轮处理头部消息的情况呢?这就要得益于我们每次调用next()方法时,prevMsg都会重新置空。
                            mMessages = msg.next;
                            if (msg.next == null) {
//如果已经是最后一个消息那么摘掉后尾部就空了 
                                mLast = null;
                            }
                        }
                        msg.next = null;
//假设A->B->C 我们需要移除B那么我们让A的下一个消息指向C,B的下一个消息指向空,防止野指针
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();//标记msg的在使用
                        if (msg.isAsynchronous()) {
                         //这个msg是个异步消息的话减少计数
                            mAsyncMessageCount--;
                        }
                        if (TRACE) {
                            Trace.setCounter("MQ.Delivered", mMessagesDelivered.incrementAndGet());
                        }
                        return msg; //把摘除的这个消息(需要处理的消息)返回
                    }
                } else {
                    // No more messages.
                    //没有消息可以处理,那么这里就会一直阻塞了
                    nextPollTimeoutMillis = -1;
                }

// Process the quit message now that all pending messages have been handled.
//如果调用了Looper.quit 那么这里就会释放资源(dispose) 消息分发循环就会退出
//为什么放在这里而不是放在方法开头?因为只有在队列空了才结束线程事件循环,这样可以避免遗留重要消息
                if (mQuitting) {
                    dispose();
                    return null;
                }

// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
//这里表示消息队列为空或者有消息但还没到处理时间,那么这个时候就成为IdleHandler,即队列没活可干
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
//首次进入空闲状态 && 队列没消息 || 有活还没来
//获取IdleHandler的数量 
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
//没有IdleHandler 阻塞等待下一条消息
                    // 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);
            }
//把注册在IdleHandlers列表里的idleHandler复制到一个数组里,提高调用效率,一般应用只会注册一个或者两个IdleHandler 所以这里分配4个

// 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 {
 //遍历所有IdleHandler然后调用其queueIdle方法(空闲时要做的任务) 这里的返回值会用于判断是否下次继续保持该IdleHandler
                    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;//清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; 
            //这里设为0保证了在IdleHandler期间一旦有新消息系统能够立马处理
        }
    }

小结

next()方法用于从MessageQueue中获取下一个可以处理的Message,每一个线程(特别是主线程)只要有Looper,都会间接通过它不断“取下一条能被处理的消息”。它控制了何时阻塞线程、何时分发消息、何时调度IdleHandler、何时退出队列。

MessageQueue里的Messgae是根据Message的When值来排列的,when是一个时间戳,遵循着与当前时间相比(系统开机至今),谁的时间越早谁优先被处理的。如果MessageQueue里没有需要处理的消息而需要处理的消息的时间(when)还没到,那么就会陷入等待。时间到了就会通过

调用nativePollOnce方法唤醒epoll上的线程进行处理。

next()处理Message的机制:如果没有barrier,那么会优先处理头部的消息。此时只需要把头节点向后移然后依次处理后续消息即可。如果有barrier,那么会引入prevMsg,如果下一个就是异步消息那么prevMsg会指向barrier。如果下一个异步消息前有很多同步消息,类似下面这样

barrier -> 同步1 -> 同步2 -> 异步 -> 同步3

此时的Message会不断遍历指向异步消息,而prevMsg则会指向它的前面也就是同步2消息。

然后会把这个异步消息取出来,取出来后会把原来指向异步的下一个也就是msg.next置为空,prevMsg也会置为空,这样就相当于从中间断开,也避免了野指针的出现。如果队列中没有message需要处理,会一直阻塞等待,后面会去判断是否要开启IdleHandler状态。

队头barrier:barrier是一个特殊的Message即它的target为空,也就是用于这个messgae对应的handler为空,称之为同步屏障。插入barrier后,后面的所有同步消息都不会被分发。业务开发中不会使用,通常系统UI,动画,输入事件相关源码会通过MessageQueue.postSyncBarrier()方法插入,然后通过removeSyncBarrier(token)方法移除barrier。

异步消息:异步消息类似于特殊VIP,有着插队的权利,我们在代码中调用的handler.sendMessage()handler.sendMessageDelayed() 其实本质上就是发了一个when为当前时间和当前时间+delayed值的Message插入MessageQueue的头部去处理。异步消息可以无视队头barrier,这样是为了某些优先级更高的操作,比如说做动画时候插入了barrier,但是input事件的优先级更高,我可以无视你的barrier阻塞。

IdleHandler:当MessageQueue中没有要处理的Message值时,会根据是否有IdleHandler判断是否进入IdleHandler状态。IdleHandler一般由应用或系统组件注册。next()内部会创建数组复制idleHandler,以提高调用效率。然后遍历IdleHandler执行其内部的queueIdle()方法,这个方法定义了IdleHandler在空闲时需要做的事情,比如初始化,预加载资源。并且如果有新的消息来它会通过让nextPollTimeoutMillis=0来立刻响应处理。nextPollTimeoutMillis值用于通过nativePollOnce方法判断是否需要阻塞或唤醒epoll上的线程。

binder缓冲:每当java进行一次跨进程通信,实际数据会被写入一个缓冲区,这些缓冲区会累积多个binder命令和响应,不及时清理的话,残留数据会一直堆积。

如果java层不清理binder数据,这些buffer数据会不断积压,导致实际内存使用量增高。

判断Binder缓冲区的内存占用情况我们在日志里可以关注以下日志。

binder mmap: 8192K  <-- 这是总共分配的binder mmap内存

2.2.5 Nativce层MessageQueue的唤醒机制 nativePollOnce

 @RavenwoodRedirect
 private native void nativePollOnce(long ptr, int timeoutMillis); 

我们前面提到了这个方法,这个方法在next()方法中很重要,会通过nextPollTimeoutMillis这个值来判断是阻塞还是唤醒线程进行处理。在MessageQueue中传入的timeoutMillis不用多说,就是一个等待时间。

这个ptr是什么呢?在next()方法刚开始的时候我们可以看到让这个ptr等于了mPtr。而这个mPtr是在MessageQueue的构造函数中mPtr = nativeInit();,可以返回到2.2.2 Native层MessageQueue的监听机制这里,可以看到返回了一个指针地址给Java层,这个指针地址是由于NativeMessageQueue在创建的时候分配内存然后返回了它的内存地址。所以我们在调用nativePollOnce的时候也可以把这个值传入,到了native层就会通过reinterpret_cast方法将这个数识别为NativeMessageQueue*的指针,从而操作关联的对象。

frameworks\base\core\jni\android_os_MessageQueue.cpp

static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
        jlong ptr, jint timeoutMillis) {
//类型转换,将java层的long类型转化为native层的指针指向NativeMessageQueue变量
    NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
    nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}

pollOnce

void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
    mPollEnv = env;
    mPollObj = pollObj;
    mLooper->pollOnce(timeoutMillis);//核心方法 调用looper进行轮询
    mPollObj = NULL; //轮询过后清除变量
    mPollEnv = NULL;
    //发生异常的话把异常抛给java层
    //其实这里对JNI这块不是很了解,在java层一般遇到报错用try catch捕获或者未捕获的交给虚拟机处理,基本哪里报错哪里就停止了。但是在JNI的语义中会把这个异常挂起 称为pending exception,代码会继续执行下去直到回到java层或者手动处理/抛出异常。
    if (mExceptionObj) {
        env->Throw(mExceptionObj);//抛出异常
        env->DeleteLocalRef(mExceptionObj);//清理异常
        mExceptionObj = NULL;
    }
}

这里looper的调用并不是framework层的looper.cpp,实现位置在

system\core\libutils\Looper.cpp

Looper::Looper(bool allowNonCallbacks)
    : mAllowNonCallbacks(allowNonCallbacks),
//是否允许无回调的文件描述符,true:允许注册没有回调的 fd,事件发生时由 pollOnce 返回给调用方(NDK 的 ALooper 常用)。
//false:要求所有 fd 必须带回调(Java/Framework 侧常用)。
      mSendingMessage(false), //标记当前是否在分发消息
      mPolling(false), //标记是否处于epoll_wait轮询中
      mEpollRebuildRequired(false),//如果遇到错误重建epoll
      mNextRequestSeq(WAKE_EVENT_FD_SEQ + 1),
//每次将epoll结果封装为Response 递增是为了区分不同的seq
//WAKE_EVENT_FD_SEQ 是为了唤醒事件保留的特殊序号
      mResponseIndex(0),//遍历的下标
      mNextMessageUptime(LLONG_MAX) //下一条消息的触发时间
{
    mWakeEventFd.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));
//eventfd 是 Linux 的“事件计数器”fd,写入一个 64-bit 整数即可让它变为“可读”,常用于跨线程唤醒 //epoll。EFD_NONBLOCK:读写不阻塞,避免死锁。EFD_CLOEXEC:exec 时自动关闭,防止句柄泄漏到子进程。
    LOG_ALWAYS_FATAL_IF(mWakeEventFd.get() < 0, "Could not make wake event fd: %s", strerror(errno));

    AutoMutex _l(mLock);
    rebuildEpollLocked();
//创建epoll实例
}

Looper::~Looper() {
}

fd:文件描述符,本质上是一个整数句柄,指向进程当前打开的某个"像文件一样"的内核对象。他不只代表磁盘文件也有可能是socket、管道、设备节点、eventfd等内核事件源。上面Looper用到的,mWakeEventFd就是一个eventfd,也就是一个事件源,这个事件源的触发条件是你往里面写一个64位的整数,那么他唤醒后就回去唤醒正在epoll_wait的线程。

epoll:一个高效的I/O多路复用机制,前面有提到过,它会通过epoll_wait一直等待,直到有就绪或者超时的fd,然后处理返回,继续下一轮。

Response:Looper的内部结构,用来描述一条就绪事件该如何处理。一条epoll返回的事件->一条Response。为什么会需要Response呢,因为epoll_wait可能一次返回一批事件,Looper先把他们整理成一个列表mResponses 然后按顺序逐条分发回调给调用方。Looper代码里

简化流程:

  • 注册阶段:addFd(fdA, 可读, 回调A), addFd(fdB, 可写, 回调B)
  • 等待:epoll_wait(timeout) 阻塞
  • 返回:假设 fdA 可读、唤醒 fd 也被写了
    • 生成两条 Response:{fdA, EPOLLIN, 回调A, seq=42}、{wakeFd, EPOLLIN, 回调Wake, seq=WAKE_EVENT_FD_SEQ}
    • 依次处理:先清空 wakeFd,再调用回调A
    • 若中途有人修改监听集合,下一轮 epoll 等到的事件会是 seq=43,不会和 42 混在一起出问题

可以看到这里对底层的唤醒也是利用了epoll的原理。上层通过nextPollTimeoutMillis值控制底层epoll的唤醒,这里的调用链如下,超时单位是毫秒,-1表示无限期等待,0表示不阻塞立刻干活。

  • Java:MessageQueue.next() 计算出 nextPollTimeoutMillis
  • JNI:nativePollOnce(ptr, nextPollTimeoutMillis)
  • C++:NativeMessageQueue::pollOnce -> android::Looper::pollOnce
  • 最终:Looper::pollInner(...) 里调用 epoll_wait(..., finalTimeout)

2.2.6 MessageQueue中的dump方法和其他常见方法

dump方法打印出消息队列里的消息详情,消息总数,消息是否处于polling以及quitting状态。

    @NeverCompile
    void dump(Printer pw, String prefix, Handler h) {
        synchronized (this) {
            pw.println(prefix + "(MessageQueue is using Legacy implementation)");
            long now = SystemClock.uptimeMillis();
            int n = 0;
            for (Message msg = mMessages; msg != null; msg = msg.next) {
                if (h == null || h == msg.target) {
                    pw.println(prefix + "Message " + n + ": " + msg.toString(now));
                }
                n++;
            }
            pw.println(prefix + "(Total messages: " + n + ", polling=" + isPollingLocked()
                    + ", quitting=" + mQuitting + ")");
        }
    }

    @NeverCompile
    void dumpDebug(ProtoOutputStream proto, long fieldId) {
        final long messageQueueToken = proto.start(fieldId);
        synchronized (this) {
            for (Message msg = mMessages; msg != null; msg = msg.next) {
                msg.dumpDebug(proto, MessageQueueProto.MESSAGES);
            }
            proto.write(MessageQueueProto.IS_POLLING_LOCKED, isPollingLocked());
            proto.write(MessageQueueProto.IS_QUITTING, mQuitting);
        }
        proto.end(messageQueueToken);
    }

我们来看一下dump方法打印出来的信息,一般用于在logcat中显示

假设我们用以下代码生成dump信息

messageQueue.dump(new PrintWriter(System.out), "[MyPrefix]", myHandler);

我们得到的日志信息如下

[MyPrefix] (MessageQueue is using Legacy implementation)
[MyPrefix] Message 0: { when=+500ms what=101 obj=foo target=android.os.Handler }
[MyPrefix] Message 1: { when=+2000ms what=202 arg1=1 target=android.os.Handler }
[MyPrefix] (Total messages: 2, polling=false, quitting=false)

when:long类型  该信息预计处理的时间

what: int类型 用于区分不同的消息,常见的就是我们代码中switch(msg.what)

obj: msg中携带的obj对象

target: 目标handler,用于处理msg

最后一行显示了信息的总数,以及是否处于休眠/退出状态。

我们再来看一下dumpDebug方法打印出来的信息,一般用于性能工具,以proto格式表示,信息和前面的大致一样。

messageQueue {
    messages {
        when: 1703776433665
        what: 101
        obj: "foo"
        target: "android.os.Handler"
    }
    messages {
        when: 1703776435500
        what: 202
        arg1: 1
        target: "android.os.Handler"
    }
    is_polling_locked: false
    is_quitting: false
}

MessageQueue中的其他方法:

    方法名作用
addIdleHandler/removeIdileHandler主线程空闲时触发任务回调
hasMessage/removeMessages检查/移除消息
postSyncBarrier/removeSyncBarrier插入/移除同步屏障
isIdle/isPolling状态检查
dump/dumpDebug输出调试信息

这里比较好理解的就不做解释了,感兴趣的可以去查看这部分源码。

isIdle和isPolling的区别是啥呢:根据我们前面的讲述 当MessageQueue中没有要处理的活时候就会去判断是否要进入IdleHandler状态,去做一些空闲时处理的任务。那么这个时候就是isIdle的状态。一般用于检查是否有在做空闲时任务。

而isPolling呢,是指IdleHandler业已经执行完了,这时候就是真的没有事情可以做了,这个时候线程就需要进入Polling/Sleep状态,一般用于检测线程是否处于休眠,放电,节省cpu的状态。

三、Handler

Handler的主要使用场景

  • 子线程工作,主线程更新UI
  • 延迟或定时任务

Handler的方法主要分为两大类:发送消息和处理消息

代码位置:base/core/java/android/os/Handler.java

3.1 Handler中的发送消息

3.1.1 post()

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

这是最常用便捷的方法,它允许直接发送一个Runnable对象,Handler会将他封装成一个Message然后添加到MessageQueue中。

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



    public boolean sendMessageAtTime(@NonNull 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);
    }

可以看到其内部调用了sendMessageDelayed方法,然后调用了sendMessageAtTime方法将msg放入MessageQueue中,并且延时为0.说明这个任务会被立马执行。类似调用如下:

// 使用 post 将 UI 更新任务切换到主线程执行
        mainHandler.post(new Runnable() {
            @Override
            public void run() {
                textView.setText(result);
            }

3.1.2 postDelayed()

postDelayed有两个重载方法

// 常用写法
public final boolean postDelayed(@NonNull Runnable r, long delayMillis) {
    return sendMessageDelayed(getPostMessage(r), delayMillis);
}

这个方法类似上面的post方法,区别在于传入了一个delayMillis的延时。

    /** @hide */
    public final boolean postDelayed(Runnable r, int what, long delayMillis) {
        return sendMessageDelayed(getPostMessage(r).setWhat(what), delayMillis);
    }

这个方法有一个hide标签,说明主要供安卓系统内部使用,对于通过Handler处理多种类型或取消特定类型的任务时有一定好处。本质上就是设置了Message的What字段,可以通过what定位,删除或者判别某种类型的Runnable消息。

上面两种都是post方法,将消息投递到消息队列中,接下来是send方法,直接发送消息。

3.1.3 sendMessage()

    /**
     * Pushes a message onto the end of the message queue after all pending messages
     * before the current time. It will be received in {@link #handleMessage},
     * in the thread attached to this handler.
     *  
     * @return Returns true if the message was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.
     */
    public final boolean sendMessage(@NonNull Message msg) {
        return sendMessageDelayed(msg, 0);
    }


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

    public boolean sendMessageAtTime(@NonNull 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);
    }

简直是和post一样的方法,那么他们的区别在于什么呢?

主要在于传入的对象不同,sendMessag传入的是Messgae对象,相较于Runnable,Message更加轻量,如果需要传递一些数据如(int,Object)那么用Message更加合适。除此以外,他还有个更加轻量化的方法sendEmptyMessage();

3.1.4 sendEmptyMessage()

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

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

    public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageAtTime(msg, uptimeMillis);
    }

可以看到也是一键三连,和sendMessage()方法不同的是它只发送了一个标识符what。

3.1.5  sendMessageAtFrontOfQueue()

除了上述几个方法,我们发现还有一个叫sendMessageAtFrontOfQueue()的方法

    public final boolean sendMessageAtFrontOfQueue(@NonNull Message msg) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtFrontOfQueue() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, 0);
    }

按理说我们已经有了sendMessage()方法已经可以把消息插到队列前面去了,为啥我们还需要sendMessageAtFrontOfQueue()方法呢? 

如果说sendMessage方法是让消息正常排队的话(无需等待的情况下)sendMessageAtFrontOfQueue()方法就是强行插队了,可以看到它插队的方式是在enqueueMessage中传入0的时间戳,而sendMessage方法传入的是系统当前时间+0的时间戳,毫无疑问直接传入0的话优先级会更高,但插队肯定会影响到原来的队列情况,只有在非常紧急特殊的情况才会用到,不然容易造成队列的阻塞。

紧急的场景有类似以下几种情况,在平常的业务开发中不会调用到这个方法。

  • 音视频播放器切换时需要立即停止旧的数据流
  • ViewRootImpl内部需要立即刷新界面
  • 紧急撤销终止Handler队列中的任务

如果只是想让一段代码在主线程执行,用 post(Runnable) 最方便。

如果需要传递数据或进行更复杂的状态管理,用 sendMessage(Message) 系列方法更灵活、性能更好。

3.2 Handler中的处理消息

3.2.1 dispatchMessage()

    /**
     * Handle system messages here.
     */
    public void dispatchMessage(@NonNull Message msg) {
        if (msg.callback != null) {
            //当我们调用post方法发送的任务,那么callback就不为空
            handleCallback(msg);
        } else {
            if (mCallback != null) {
            //当我们在构造handler的时候传入了callback 那么就会走这里
            //如果callback成功处理了消息就不会触发后续的handleMessage方法
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            //这里就是sendMessage传过来的Message了或者上面的callback没有成功处理的
            handleMessage(msg);
        }
    }

当Looper从MessageQueue中取出一个消息后 他不会直接调用handleMessage()方法而是调用dispatchMessage()方法,这个方法内部会决定消息该由谁来处理。

知道它的原理后我们其实可以在子类重写这个方法去做一些操作(根据业务需要)比如说

1、消息过滤或拦截

@Override
public void dispatchMessage(Message msg) {
    // 假设我们有一个标志位来决定是否暂停处理所有消息
    if (isPaused) {
        Log.d(TAG, "Handler is paused. Dropping message: " + msg.what);
        return; // 直接返回,消息被丢弃
    }
    
    // 如果不暂停,则正常分发
    super.dispatchMessage(msg);
}

2、加入自定义的分发逻辑

@Override
public void dispatchMessage(Message msg) {
    // 引入我们自己的最高优先级处理
    if (mGlobalListener != null && mGlobalListener.onHandleMessage(msg)) {
        return;
    }
    
    // 如果我们的监听器不处理,再走父类的标准流程
    super.dispatchMessage(msg);
}

有一个注意点就是一定要调用super.dispatchMessage(msg) 不然handler的工作机制就失效了。

3.2.2 handleMessage()

    /**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(@NonNull Message msg) {
    }

从注释上我们能看出,子类一定要去实现这个方法。但他既不是abstract抽象类,又不是一个接口。为什么不把他定义成抽象类或者接口呢?

这个在dispatchMessage()的方法中已经告诉我们了,只有在最后的时候才会去调用handleMessage()方法,假设我们是用的post方法或者在构建handler的时候已经传入了callback,那么处理Message就将有callback来做,也就自然而然的不需要重写handleMessage方法了。

handler消息派发的流程为

1、handler往消息队列中投递消息(send/post)

2、Looper不断循环获取消息

3、拿到消息后根据msg.target分给对应handler处理,调用它的dispatchMessage

我们在平常代码中一般都会重写handleMessage方法,去执行对应Message的处理方式,比如:

@Override
    public void handleMessage(@NonNull Message msg) {
        super.handleMessage(msg);
        // 根据 msg.what 来判断是哪种消息
        switch (msg.what) {
            case 1:
                // 处理消息,更新 UI
                String data = (String) msg.obj;
                activity.textView.setText("收到消息: " + data);
                break;
            case 2:
                // 处理另一种消息
                break;
        }
    }
}

3.2.3 Handler中的其他方法

方法名作用
消息队列的移除
removeCallbacks(Runnable r)移除队列中特定的 Runnable 任务。如果你用 postDelayed 发送了一个延时任务,但中途想取消它,就用这个方法。
removeMessages(int what)移除队列中 what 值匹配的所有 Message
removeCallbacksAndMessages(Object token)这是一个非常重要和常用的清理方法。token 是一个标识符,通常我们传入 null。当传入 null 时,它会移除这个 Handler 发送到队列中的所有 Callbacks 和 Messages。通常在 Activity 的 onDestroy() 方法中调用 handler.removeCallbacksAndMessages(null) 来进行“一键清理”
消息队列的查询
hasMessages(int what)检查队列中是否存在指定 what 值的消息
hasCallbacks(Runnable r)检查队列中是否存在指定的 Runnable 任务
hasMessagesOrCallbacks()检查队列中是否有任何待处理的消息或回调(不常用)
Message对象的获取
obtainMessage(...)提供了一系列的重载方法,从全局的消息池中获取一个可复用的 Message 对象,和Message章节的obtain构造函数类似,值得一提的是,如果传入的是空参,那么就默认指定当前handler为Message的target
创建/获取Handler实例
getMain()获取主线程的Handler
createAsync(Looper looper)创建一个异步 Handler。它发送的消息(称为异步消息)不会被 UI 线程的同步屏障(Sync Barrier)所阻塞。这在处理 UI 绘制、输入事件等高优先级系统任务时非常重要,可以确保动画流畅。常规应用开发中较少直接使用。
getLooper()获取当前Handler绑定的Looper
调试方法
getMessageName(Message message)将Message对象转化为一个可读的字符串描述
dump(Printer,String)将Handler消息队列中等待处理的消息打印出来
特殊场景使用
runWithScissors(Runnable r,long timeout)将一个Runnable任务post到Handler所在线程执行,并阻塞当前线程直到该任务在目标线程执行完毕或超时。是一种强制的线程同步方式

3.2.3.1 createAsync(Looper looper)

    @NonNull
    public static Handler createAsync(@NonNull Looper looper) {
        if (looper == null) throw new NullPointerException("looper must not be null");
        return new Handler(looper, null, true);
    }

创建异步Handler,这个机制主要由安卓系统使用,最典型的场景就是UI渲染,可以忽略同步屏障,能够保证自身消息的顺序。这个方法的核心在于最后一个参数传入了true。

    @UnsupportedAppUsage
    public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
        this(looper, callback, async, /* shared= */ false);
    }

当async值为true时,这个handler实例之后发送的所有消息在内部都会被标记为异步,在处理消息的时候就会根据这个标记来决定是否让该消息越过同步屏障。

    private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();

        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
3.2.3.2 getMessageName(Message message)
    @NonNull
    public String getMessageName(@NonNull Message message) {
        if (message.callback != null) {
            return message.callback.getClass().getName();
        }
        return "0x" + Integer.toHexString(message.what);
    }

如果是使用post方法传入的Message那么返回的示例为

"com.example.MyActivity$1"      Runnable的完整类名

如果是使用send方法传入的则是

"0x64"           标识message.what的十六进制

3.2.3.3 dump(Printer,String)

打印出Handler及其关联的Looper的当前状态。

    public final void dump(@NonNull Printer pw, @NonNull String prefix) {
        pw.println(prefix + this + " @ " + SystemClock.uptimeMillis());
        //打印出Handler对象自身的信息
        if (mLooper == null) {
            pw.println(prefix + "looper uninitialized");
        } else {
            mLooper.dump(pw, prefix + "  ");
         //调用了Looper里的dump方法
        }
    }


    //Looper.java
    public void dump(@NonNull Printer pw, @NonNull String prefix) {
        pw.println(prefix + toString());
        mQueue.dump(pw, prefix + "  ", null);
    }


Printer是一个简单的接口,代表一个打印目的地,这样的设计非常灵活,意味着dump的输出不局限于logcat,比如传入LogPrinter(打印到Logcat),StringBuilderPrinter(打印到StringBuilder)。

prefix是一个字符串前缀,用于在每一行输出时添加在行首。

可以看到这里最终调用了MessageQueue中的dump方法,见2.2.6 MessageQueue中的dump方法和其他常见方法  这里只是提供了一个接口。

四、Looper

从前面的篇章中我们已经很明了Message,MessageQueue,Handler之间的关系了。MessageQueue存储Message,Handler会去往MessageQueue里面丢Message和拿Message出来,并且根据Message里的target属性去指定某个Handler处理。这是前面一段的总结,那么Looper呢,我们都知道Looper是那个循环一直做这件事的人,它是怎么和Handler,Message这些关联的呢?

4.1 Looper与Handler的绑定

第一种,显示关联,比如

Handler mainHandler = new Handler(Looper.getMainLooper());

我们在构造handler的时候就传入Looper的参数进去,告诉Handler你要和哪个looper进行绑定了。上述代码就表示mainHandler会和主线程的Looper绑定。

第二种,隐式关联,也是我们最常用的

Handler myHandler = new Handler(); 

其实Looper在Handler构建的时候就已经和Handler绑定上了,也就是说Looper是伴随着Handler的出现而出现的。从出生的那一刻起他俩就帮在一块了。那么具体是咋实现的呢。

    public Handler(@Nullable 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();//主要关注这一行
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
        mIsShared = false;
    }

这是Handler的构造函数,我们可以看到当我们new了一个Handler时,其内部有一个mLooper变量,是由Looper.myLooper()方法构造而来的。

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

 // sThreadLocal.get() will return null unless you've called prepare().
    @UnsupportedAppUsage
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

从这个注释我们可以知道,如果我们没有调用prepare方法,那么当我们在Handler的构造函数里myLooper就只会拿到一个null值

那么他就会报错

java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

所以如果我们要使用new Handler()这个构造方法来构造Handler的话就需要在前面执行Looper.prepare ,现在这个方法已经被废弃了 官方更推荐我们使用new Handler(Looper looper);

方法。隐式关联实际上是试图绑定当前线程的Looper,而当前线程有没有Looper,还要看我们是否调用Looper.prepare()方法将准备的Looper投放进去。

这里其实还有一个问题,为什么主线程就没调用过Looper.prepare()方法呢,那是因为主线程在ActivityThread.main()里就为主线程创建了一个Looper。见2.2.1 主线程MessageQueue的创建

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

可以看到这里和主线程不一样的是 这里传入的参数是true,而主线程传入的是false,这个布尔值其实代表了线程是否允许退出。到此,Looper与Handler之间的绑定就比较清晰了。

 4.2 Looper的启动与退出

启动消息循环,这段代码会阻塞线程,让其进入无限循环,不断从MessageQueue中取出消息交给Handler处理,也是Looper中最核心的方法。

4.2.1 loop();

/**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    @SuppressWarnings({"UnusedTokenOfOriginalCallingIdentity",
            "ClearIdentityCallNotFollowedByTryFinally",
            "ResultOfClearIdentityCallNotStoredInVariable"})
    public static void loop() {
        final Looper me = myLooper();
        //获取当前线程的looper
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }

        //检查是否重复调用loop方法
        if (me.mInLoop) {
            Slog.w(TAG, "Loop again would have the queued messages be executed"
                    + " before this one completed.");
        }

        me.mInLoop = true;//标记循环开始

        // 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 = getThresholdOverride();

        me.mSlowDeliveryDetected = false;
        //这里是安卓的性能监控 如果消息处理的时间超过某个阈值会在日志中警告
        for (;;) {
        //核心方法,loopOnce的返回值表示是否成功处理消息
            if (!loopOnce(me, ident, thresholdOverride)) {
                return;
            }
        }
    }

4.2.2 loopOnce()

/**
     * Poll and deliver single message, return true if the outer loop should continue.
     */
    @SuppressWarnings({"UnusedTokenOfOriginalCallingIdentity",
            "ClearIdentityCallNotFollowedByTryFinally"})
    private static boolean loopOnce(final Looper me,
            final long ident, final int thresholdOverride) {
        Message msg = me.mQueue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return false;
        }
        //从消息队列中取出下一个消息,如果为空就停止

        // 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);
        }
      //用于调试的日志打印器,如果调用了Looper.setMessageLogging(),那么会给每条消息的处理前后都打上日志,追踪消息流
        // Make sure the observer won't change while processing a transaction.
        final Observer observer = sObserver;

        final long traceTag = me.mTraceTag;
        long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
        long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;

        final boolean hasOverride = thresholdOverride >= 0;
        if (hasOverride) {
            slowDispatchThresholdMs = thresholdOverride;
            slowDeliveryThresholdMs = thresholdOverride;
        }
        final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0 || hasOverride)
                && (msg.when > 0);
        final boolean logSlowDispatch = (slowDispatchThresholdMs > 0 || hasOverride);

        final boolean needStartTime = logSlowDelivery || logSlowDispatch;
        final boolean needEndTime = logSlowDispatch;

        if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
            Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
        }
        //这一块是性能检测,logSlowDelivery表示预计执行到实际开始执行的时间差过大,说明被前面的消息阻塞了,logSlowDispatch表示处理消息花费的时间太长,说明处理的方法里有耗时操作
        final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
        final long dispatchEnd;
        Object token = null;
        if (observer != null) {
            token = observer.messageDispatchStarting();
        }
        long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
        //为电池消耗归因 系统需要知道是谁唤醒了cpu
        try {
            msg.target.dispatchMessage(msg);
        //调用handler的dispatchMesage方法进行分发
            if (observer != null) {
                observer.messageDispatched(token, msg);
            }
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } catch (Exception exception) {
            if (observer != null) {
                observer.dispatchingThrewException(token, msg, exception);
            }
            throw exception;
        } finally {
            ThreadLocalWorkSource.restore(origWorkSource);
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        //记录归因和结束trace记录
        }
        if (logSlowDelivery) {
            if (me.mSlowDeliveryDetected) {
                if ((dispatchStart - msg.when) <= 10) {
                    Slog.w(TAG, "Drained");
                    me.mSlowDeliveryDetected = false;
                }
            } else {
                if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
                        msg)) {
                    // Once we write a slow delivery log, suppress until the queue drains.
                    me.mSlowDeliveryDetected = true;
                }
            }
        }
        if (logSlowDispatch) {
            showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
        }

        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }
        //如果发生了logSlowDelivery或logSlowDispatch就把这条message打印出来
        // Make sure that during the course of dispatching the
        // identity of the thread wasn't corrupted.
        final long newIdent = Binder.clearCallingIdentity();
        //进行身份检查,确认binder调用身份没有被修改
        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();
         //将msg归还到池中方便下次复用
        return true;
    }

这个非常清楚的解释了Looper是如何工作的,它首先不断的从MessgaeQueue中拿消息,然后调用message的对应handler去处理消息,期间会做好性能的监控,用完后还会把Message还回去方便下一次的复用。

4.2.3 quit()/quitSafely()

Looper的退出方法,可以看到是调用了MessageQueue里的quit方法,前面没留意这个方法

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

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

//MessageQueue.java
    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);
        }
    }

可以看到这两个退出方法传入了不同的参数,如果是true的话会调用removeAllFutureMessagesLocked()方法,这个方法不会立即终止消息队列,而是等消息队列中的所有消息都处理完了,所有未来的延迟消息会被丢弃,比较优雅的退出,也是比较推荐的写法。

如果传入的是false,那么就会调用removeAllMessagesLocked()方法直接丢弃队列中所有未处理的消息。removeAllFutureMessagesLocked()内只是加了个条件判断,如果是在排队的消息就循环处理他们,不是的话就调用removeAllMessagesLocked()直接将他们移除,移除的机制其实就是让MessageQueue中的参数初始化。

结语

这篇文章断断续续写了很久,详细介绍了Looper,MessageQueue,Message,Handler内的通信机制和常用方法,如果你是刚踏入安卓系统的萌新(虽然我也是),那么这篇文章应该会给你带来很大的收获,我也没想到最后会码上那么多字,里面的很多疑问其实都是我在写的过程中遇到的然后去查了一下。本文的代码都来源于谷歌的源码,在这里贴上源码网站的链接。

Android Code Search

 码字不易,希望大家多多点赞关注,感谢!

本文参考链接:消息处理机制 Handler、Looper 和 MessageQueue_handler messagequeue 作用-CSDN博客

一、消息处理中的主要概念 “消息”是在两台计算机间传送的数据单位。消息可以非常简单,例如只包含文本字符串;也可以更复杂,可能包含嵌入对象。 消息被发送到队列中。“消息队列”是在消息的传输过程中保存消息的容器。消息队列管理器在将消息从它的源中继到它的目标时充当中间人。队列的主要目的是提供路由并保证消息的传递;如果发送消息时接收者不可用,消息队列会保留消息,直到可以成功地传递它。 “消息队列”是 Microsoft 的消息处理技术,它在任何安装了 Microsoft Windows 的计算机组合中,为任何应用程序提供消息处理和消息队列功能,无论这些计算机是否在同一个网络上或者是否同时联机。 “消息队列网络”是能够相互间来回发送消息的任何一组计算机。网络中的不同计算机在确保消息顺利处理的过程中扮演不同的角色。它们中有些提供路由信息以确定如何发送消息,有些保存整个网络的重要信息,而有些只是发送和接收消息。 “消息队列”安装期间,管理员确定哪些服务器可以互相通信,并设置特定服务器的特殊角色。构成此“消息队列”网络的计算机称为“站点”,它们之间通过“站点链接”相互连接。每个站点链接都有一个关联的“开销”,它由管理员确定,指示了经过此站点链接传递消息的频率。 “消息队列”管理员还在网络中设置一台或多台作为“路由服务器”的计算机。路由服务器查看各站点链接的开销,确定经过多个站点传递消息的最快和最有效的方法,以此决定如何传递消息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值