Handler 源码解析,从入门到几乎入门
  mkIZDEUN7jdf 2023年11月24日 16 0

Android Handler 源码解析

在 Android 中,Handler 是一种强大的机制,用于在不同的线程之间进行通信。通过 Handler,你可以轻松地将任务从一个线程发送到另一个线程,通常用于在后台线程执行任务后更新UI。同时handler机制也是Android主线程运行的原理,了解了主线程的运行原理也就可以知道leakCancry的实现原理。

1. Handler 的基本原理

在 Handler 的背后,有三个核心概念:消息队列(Message Queue)、消息(Message)、Looper。让我们逐一了解它们的作用。

1.1 消息队列(Message Queue)

消息队列是一个先进先出(FIFO)的数据结构(在MessageQuene中使用链表来实现的),用于存储待处理的消息。每个 Handler 关联一个消息队列,这个队列负责存储和处理发送到该 Handler 的消息。

1.2 消息(Message)

消息是 Handler 处理的基本单元。每个消息都包含一个标识符(what)、处理消息的目标 Handler(target)、可选的数据(obj)以及处理消息的方法(callback)等信息。消息对象通过消息队列在不同线程间传递。

1.3 Looper

Looper 是一个线程局部的类,用于管理消息队列。一个线程只能有一个 Looper,而主线程默认已经有一个 Looper。在子线程中使用 Handler 之前,你通常需要调用 Looper.prepare()Looper.loop() 来初始化和启动消息循环。

2. Handler 的创建与使用

2.1 创建 Handler

在主线程中创建 Handler 时,它会自动与主线程的 Looper 关联。在其他线程中使用 Handler 时,你需要显式地将 Handler 与该线程的 Looper 关联。

// 在主线程中创建 Handler
Handler handler = new Handler();

// 在子线程中创建 Handler
Handler handler = new Handler(Looper.getMainLooper());

2.2 发送消息

使用 Handler.sendMessage(Message msg) 方法可以向消息队列发送消息。

Message message = handler.obtainMessage();
message.what = MESSAGE_ID;
message.obj = someObject;
handler.sendMessage(message);

2.3 处理消息

通过重写 Handler.handleMessage(Message msg) 方法,可以处理收到的消息。

Handler handler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        // 处理消息
    }
};

3. Handler 的核心源码解析

一下源码基于Android最新版本的SDK34 ,不同版本的sdk代码结构有所不同,但是基本代码逻辑是不变的

3.1 首先找到ActivityThread

ActivityThread 是 Android 系统中一个关键的类,它负责应用程序的主线程(UI 线程)的管理,以及处理与应用程序组件(如 Activity、Service、BroadcastReceiver 等)的生命周期相关的任务。在 Android 中,每个应用程序都会有一个 ActivityThread 实例,它是整个应用程序的入口点,java中的main方法就是在里面调用的。

代码结构如下

public class ActivityThread {
public static void main(String[] args) {

    // 1.在主线程中创建消息循环(Looper)
    Looper.prepareMainLooper();
    // 创建ActivityThread对象
    ActivityThread thread = new ActivityThread();
    
    // 初始化主线程的一些参数和状态
    thread.attach(false);

    // 2.启动消息循环,处理消息队列中的消息
    Looper.loop();
    
    // 程序执行到这里时,不会继续执行,因为 Looper.loop() 是一个无限循环
}
}

3.1.1 Looper.prepareMainLooper();//1.创建Looper

Handler 源码解析,从入门到几乎入门_Android

通过ThreadLocal 创建一个线程唯一(线程内单例)的Looper 对象,他是线程安全的,和synchronized区别是

  • synchronized机制采用了以时间换空间的方式
  • ThreadLocal采用了以空间换时间的方式

同时也可以看到prepare 不能调用两次,否则会抛出异常

同时在Looper的构造函数里面新建了一个MessageQueue

Handler 源码解析,从入门到几乎入门_Android_02

那么也就是说:一个线程中Looper对象唯一,也唯一对应一个MessageQueue对象

这里 可以可以得出结论 1个Thread----> 1个Looper------> 1 个MessageQueue----->多个Message---->多个Handler

3.1.2 Looper.loop(); 消息循环


 public static void loop() {
        //获取到上一步创建的Looper对象
        final Looper me = myLooper();
        for (;;) {//死循环
            if (!loopOnce(me, ident, thresholdOverride)) {
                return;
            }
        }
    }



 private static boolean loopOnce(final Looper me,
            final long ident, final int thresholdOverride) {
        Message msg = me.mQueue.next(); // 1.might block
        if (msg == null) {//调用quit会导致msg为null
            // No message indicates that the message queue is quitting.
            return false;
        }
        //。。省略
        msg.target.dispatchMessage(msg);//消息分发处理
        msg.recycleUnchecked();//消息回收
        return true;//返回true继续死循环
 }

why 死循环?因为我们程序是需要一直运行的,那一直循环,不会很浪费系统资源吗?熟悉Looper的Android开发都知道,Android的主线程是有阻塞和唤醒机制的,在没有消息处理的时候Android主线程会进入阻塞,让出cpu时间,不会浪费系统资源,等到下一条消息到来的时候(enqueueMessage)会唤醒线程;

Looper的阻塞涉及到Linux pipe/epoll机制

下面就看一下在消息机制里面是如何阻塞和唤醒的

3.2 MessageQueue中的next()

尝试检索下一条消息。如果找到则返回(没有消息则进入阻塞,释放cpu时间)

nativePollOnce(ptr, nextPollTimeoutMillis);

这个函数会让线程进入休眠状态

nextPollTimeoutMillis 为 -1 表示永久等待(直到被唤醒)/0表示不等待

Message next() {
    int nextPollTimeoutMillis = 0;
     for (;;) {
         //阻塞
         nativePollOnce(ptr, nextPollTimeoutMillis);
         synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;//取到链表头
                //检查链表里面是否存在异步消息 target==null代表这个消息是个异步消息屏障
                if (msg != null && msg.target == null) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    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 {
                        //获取到消息
                        // Got a message.
                        //把消息从链表中删除
                        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;
                }
             ............
              mBlocked = true;
              continue;//进入下一次循环
         }
}

代码较长,其实总结就是

在next方法中,从队列中读取消息时,会先检查是否存在同步屏障。如果存在同步屏障,优先返回队列中的异步消息,并将该异步消息从队列中移除。如果队列中没有同步屏障,则返回队列中的同步消息,并将该同步消息从队列中移除

什么是同步和异步消息呢,首先看Message

3.3Message

其中两个重要属性

Handler target;//该消息的发送者
 Message next;//消息是一个链表
 public long when; //消息的执行时刻

消息对象还具有回收复用机制

在发送消息的时候 使用 handler.obtain 而不是直接 new Message()//他会先尝试从里面回去,取不到才new

loop 在处理完一条消息之后会调用msg.recycleUnchecked();来回收消息

private static Message sPool;//消息池链表

Message 内部维护了一个消息池链表,回收后的消息都会进入里面

消息还分为同步消息和异步消息

3.3.1同步消息

message中有一个重要的属性 when,表示该消息执行的时刻

Handler 的各种sendMessage方法 最终都会调用到

public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;//1.
    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);
}
boolean enqueueMessage(Message msg, long when)

所以when 代表 这条消息什么时候执行(是一个时刻)

所以when 代表 这条消息什么时候执行(是一个时刻),例如有些消息需要延迟执行

  1. 可以根据这条消息什么时候执行吧消息插入到队列中合适的位置
  2. 如果队列为空或新消息的等待时间小于队头消息的等待时间,新消息将被放置在队列的头部。如果新消息的等待时间大于队头消息的等待时间,新消息将从队头开始依次与队列中的消息进行比较,直到找到合适的位置插入

这里有一个结论是,messageQueue里面的message会按照链表执行的时间顺序从链表头到链表尾排序,链表链表头的消息会最先执行,后面分析enqueueMessage会说到

3.3.2异步消息

异步指的是,他会优于其他普通消息执行,在他执行完毕之前其他消息无法执行

异步消息的逻辑也很简单

通过postSyncBarrier()来创建一个屏障

本质上也是给链表中插入一条消息,但是这条消息的特殊之处在于这条message的traget也就handler为null

稍后通过removeSyncBarrier() 来移除掉屏障

这样通过message.setAsynchronous(true) 设置的异步消息就会优先执行

Android的刷新机制就是这样做的,这样可以有效的避免卡顿,毕竟视觉效果才是第一位嘛

大致来说呢就是 在 Vsync信号来的时候,设置同步屏障 ,之后 measure layout draw 绘制流程 包装成一个异步消息,执行完毕之后移除屏障

3.3 enqueueMessage

boolean enqueueMessage(Message msg, long when) {
     synchronized (this) {
         //消息标记为正在使用
         	msg.markInUse();
         //设置执行时间
            msg.when = when;
         //下面的链表操作为按照时间顺序把message插入到链表合适的位置
            Message p = mMessages;
            boolean needWake;
         //插入到链表头
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;//没有消息||当前消息需要立刻执行||当前消息比消息队列中任何一个消息都要早
            } else {
                // Inserted within the middle of the 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();
                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;
    }
}

3.4 idle Handler

idleHandler 和普通Handler不同,线程空闲的时候才会执行

具体到代码

//此时处于空闲状态
// 1.下一个 Message 执行时间未到
// 2.没有Message 了
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)];
    }
    // 把 mIdleHandlers 拷贝到 mPendingIdleHandlers 中
    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;

使用方法

public class MyIdleHandler implements MessageQueue.IdleHandler {
    @Override
    public boolean queueIdle() {
        // 在主线程空闲时执行任务
        return false; // 返回 true 表示继续监听空闲事件,返回 false 表示不再监听
    }
}

MyIdleHandler myIdleHandler = new MyIdleHandler();
Looper.myQueue().addIdleHandler(myIdleHandler);

// 在适当的时机注销
Looper.myQueue().removeIdleHandler(myIdleHandler);

3.5 handler 的dispatchMessage

public void dispatchMessage(@NonNull Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {//执行Handler内部的callback
            if (mCallback.handleMessage(msg)) {//返回false 的话handler Message 还会被调用
                return;
            }
        }
        handleMessage(msg);
    }
}

类似责任链模式

如果msg有callBack 只处理message 的callback

否则 看Handler自己 有没有callback

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月24日 0

暂无评论

推荐阅读
mkIZDEUN7jdf