美文网首页
Handler(一)源码解析

Handler(一)源码解析

作者: NIIIICO | 来源:发表于2021-11-12 12:02 被阅读0次

    前言

    Android Handler基本是面试必问的知识点,也是了解Android源码的第一步,那么Handler到底是什么呢?接下来我们一探究竟。

    Handler基础

    Handler主要涉及4个类,分别是Handler、Looper、Message、MessageQueue。

    Handler Looper Message
    MessageQueue

    Handler发送消息-处理消息流程

    那么它们是如何相互作用,来实现Handler的功能的呢?

    Handler发送消息,处理消息流程

    1、通过调用sendMessage、post等方法,会最终调用到Handler的enqueueMessage方法,此方法会向MessageQueue的mMessages中新增一个节点。


    发送消息

    2、当线程中Looper.loop调用时,会启动一个死循环,轮询获取Message信息。

    public static void loop() {
        ········
        final Looper me = myLooper();
        final MessageQueue queue = me.mQueue;
        for (;;) {
            Message msg = queue.next(); // might block
            ········
            msg.target.dispatchMessage(msg);
        }
        ········
    }
    

    3、当获取到Message消息后,通过msg.target.dispatchMessage(msg)方法,回调给Handler。

    初始化流程

    初始化流程

    1、Handler初始化的时候,需要传入Looper,或者通过Looper.myLooper()获取当前线程的Looper;并得到looper.mQueue对象。

    public Handler(@NonNull Looper looper) {
        this(looper, null, false);
    }
    
    public Handler(@Nullable Callback callback, boolean async) {
        ........
        mLooper = Looper.myLooper();
        mQueue = mLooper.mQueue;
        ........
    }
    

    2、Looper初始化的时候,会创建MessageQueue对象。

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

    3、在Handler发送消息时,会将handler赋值给Message.target

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

    Handler为什么会内存泄露

    由此,我们可以看到如下的持有链,当长生命周期的对象(Thread)持有短生命周期的对象(真正处理回调的类,如Activity),内存泄漏便发生了。

    Handler的持有链

    Handler常见问题

    1、Message可以如何创建,哪种效果更好?

    message可以通过new Message()、Message.obtain()、handler.obtainMessage()创建,用后两种方式创建效果比较好。

    Message回收池

    在Message中,通过单向链表构建了Message的缓存池,防止对象频繁创建、销毁造成内存抖动。

    public static final Object sPoolSync = new Object();// 锁
    private static Message sPool;// 缓存池
    private static int sPoolSize = 0;// 缓存的个数
    private static final int MAX_POOL_SIZE = 50;// 最大缓存个数
    
    // 通过缓存池获取Message对象,若缓存池为null,则创建新的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();
    }
    
    // 回收Message对象
    void recycleUnchecked() {
        // Mark the message as in use while it remains in the recy
        // Clear out all other details.
        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;
                sPoolSize++;
            }
        }
    }
    

    2、多个Handler往MessageQueue中添加数据(发消息时各个Handler处于不同线程),内部如何保证安全的?

    我们翻看MessageQueue的源码,即可发现,在向MessageQueue的mMessages添加数据时,进行了加锁处理。

    boolean enqueueMessage(Message msg, long when) {
        ········
        synchronized (this) {
        ········
        }
        return true;
    }
    

    3、Looper.loop为什么不会导致主线程卡死

    app启动流程

    当App启动时,Zygote进程会fork新的进程,然后通过ActivityThread的main方法进入应用程序。在main方法中,会启动Looper.loop,而在loop之前,会通过thread.attach绑定ActivityManagerService。当系统有事件响应时,便会通知ActivityThread的mH进行处理。

    public static void main(String[] args) {
        ········
        Looper.prepareMainLooper();
        ········
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);
        ········
        Looper.loop();
    }
    
    // 用于处理AMS响应的事件
    class H extends Handler {
        public static final int BIND_APPLICATION        = 110;
        public static final int EXIT_APPLICATION        = 111;
        public static final int RECEIVER                = 113;
        public static final int CREATE_SERVICE          = 114;
        public static final int SERVICE_ARGS            = 115;
        public static final int STOP_SERVICE            = 116;
        ········
    }
    
    

    4、一个线程有几个Looper?可以有几个Handler?

    一个线程只能有一个Looper,Looper.loop的时候,会形成死循环,此时即使有多个Looper也没有意义。那是怎么保证Looper的关系呢?通过ThreadLocal保证Looper和Thread的关系。

    而一个线程可以有很多个Handler,只要在初始化的时候得到线程的Looper即可。

    // sThreadLocal.get() will return null unless you've called prepare().
    @UnsupportedAppUsage
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    
    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));
    }
    
    /**
     * Return the Looper object associated with the current thread.  Returns
     * null if the calling thread is not associated with a Looper.
     */
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
    

    5、Handler.postDelay后,消息队列会有什么变化?

    根据Handler消息发送流程,我们知道,无论通过何种方法调用,最终都会调用到enqueueMessage方法。
    观察一下源码,参数中有一个uptimeMillis参数;它是通过SystemClock.uptimeMillis() + delayMillis计算出来的。

    private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        // 调用MessageQueue的enqueueMessage
        return queue.enqueueMessage(msg, uptimeMillis);
    }
    
    public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        // 计算更新时间
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
    

    当把消息插入队列时:
    1、如果消息队列为null 或 更新时间是0 或 当前消息更新时间<队头元素的更新时间,则将消息添加至队头,并设置唤醒标识。
    2、否则遍历消息队列中的元素,按时间顺序将消息插入队列中,这种情况不需要唤醒。
    3、如果最终唤醒标识为true,会进行唤醒逻辑处理。

    boolean enqueueMessage(Message msg, long when) {
        ········
        synchronized (this) {
            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            // 如果消息队列为null 或 更新时间是0 或 当前消息更新时间<队头元素的更新时间
            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 
                // up the event queue unless there is a barrier at the head of the que
                // 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.
            // 唤醒,Message msg = queue.next()停止阻塞
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }
    

    相关文章

      网友评论

          本文标题:Handler(一)源码解析

          本文链接:https://www.haomeiwen.com/subject/zqsizltx.html