Handler 解析

356 阅读5分钟

基础概念

  • Handler 消息的发送者和接收者
  • Message 消息的载体
  • MessageQueue 消息链表按照when的时间进行插入
  • Looper 泵站从MessageQueue中提取消息

Handler

带着问题读源码

  • message 消息接收的优先级
  • 异步消息怎么创建
  • 接收消息的优先级

构造方法

首先看构造方法, 构造方法最终有2种,可以说一种是传入Looper的 一种是不传入Looper的 2种的差异就是 是否要来自己获取Looper。(难道这种不能通过重载来实现吗)

小知识点:

  • 一个线程只有一个 Looper 和一个MessageQueue,但是可以有多个Handle。
  • Message是通过target来找到指定Hander 进行回调的

第一种:


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


/**
 * @param looper 线程切换的关键,一个线程中只有一个,并且只能初始化一次
 * @param callback Message的回调,这个只能从构造方法中传入
 * @param async 是否是异步消息。 在队列中异步消息有更高的优先级
 */
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

发送消息

发送消息最终都会调用sendMessageAtTime,time是 SystemClock#uptimeMillis 开机之后的时间 time 也是插入队列的依据  

第一种

/**
 * @param r 回调的Runnable 这个最终指定的是 Message.callback
 * @param token 这个是用来取消时候用的 removeCallbacksAndMessages
 * @param uptimeMillis time 距离开机之后多久
postAtTime(@NonNull Runnable r, @Nullable Object token, long uptimeMillis)

第二种

/**
 * @param msg 要发送的消息
 * @param uptimeMillis 距离开机之后多久用来队列排队
sendMessageAtTime(@NonNull Message msg, long uptimeMillis)

第三种
放在队列首部的消息,这个里面也就是把 uptimeMillis 设置为 uptimeMillis 为0

postAtFrontOfQueue(Runnable r)
sendMessageAtFrontOfQueue(Message msg)

接收消息

优先级:

  • Message 自带的callback,callback的来源:post(Runnable r) 中的 Runnable
  • mCallback 构造方法中传入的mCallback
  • Handler 方法 handleMessage
public void dispatchMessage(@NonNull Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

入队

这里只有一个关键的地方,就是经常说的Handler为什么会引起来内存泄漏的原因
msg.target = this; 这里让Message持有了Handler对象。
但是Message是会被丢到了队列中。那么当Activity退出后Message依然还在队列中时就会引起内存泄漏。
原因:当系统GC的时候,因为 Handler有对Activity引用,Message又引用了Handler。那么系统就不会对Activity 进行回收了

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

Message

一个的单链表模式,通过next来获取下一个Message。而 MessageQueue 并不是消息队列而是处理消息队列的工具
这里大多为实体类唯一可以说的是缓存应用

写入缓存

void recycleUnchecked() {
    // Clear out all other details.
    ......
    ......
    synchronized (sPoolSync) {
        // 如果没有达到最大值 MAX_POOL_SIZE 的值为50
        if (sPoolSize < MAX_POOL_SIZE) {
            // sPool 是一个Message对象
            // 这个把当前消息放在链表的首部
            
            // 第一个操作把当前的sPools放入next节点
            next = sPool;
            // 第二个操作把首节点指向首部
            sPool = this;
            sPoolSize++;
        }
    }
}

获取缓存消息

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;
        }
    }
    // 如果没有缓存那么直接使用Message
    return new Message();
}

MessageQueue

MessageQueue其实并不是一个消息队列。他只是Message链表的处理的一个类 这个里面主要有2块 1. Message 入队 2. 取消息

 

入队

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;
        // 第一种情况直接加入到 当排头加入到队列首部
        // 1. p == null 也就是当前没有消息列表
        // 2. when == 0 加入到队列首部,在Handler中说过 postAtFrontOfQueue(Runnable r) sendMessageAtFrontOfQueue(Message msg) 这2种方式 when为0
        // 3. 当前消息的When是小于 目前队首的When
        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循环来插入链表
            // 确认位置的2个点
            // 1. 如果没有子节点 那么认为已经是在队尾 直接插入即可
            // 2. 当前的时间小于下一个节点的时间
            for (;;) {
                prev = p;
                p = p.next;
                if (p == null || when < p.when) {
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            // 手拉手把前后2个节点给拉起来
            msg.next = p; // invariant: p == prev.next
            prev.next = msg;
        }

        // We can assume mPtr != 0 because mQuitting is false.
        // 是否需要进行唤醒 线程等待需要就看 mBlocked, 这里会在取消息的时候进行赋值
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

获取消息

Message next() {
    ......
    ......
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
        // 线程阻塞,如果nextPollTimeoutMillis == -1 会一直等待,如果nextPollTimeoutMillis > 0 等待多少毫秒
        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的时候是一个消息屏障(这个是系统可用,APP层不可用)
            // 这里是主要优先处理紧急消息
            // 这里的意思是 如果当前是首部消息为屏障消息 那么就先从队列中获取其中的异步消息进行处理
            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 {
                    // 获取到了一个消息,那么就不需要进行 blocked
                    mBlocked = false;
                    // prevMsg != null ,这个什么时候回出现呢?
                    // 只有当获取到屏障消息时 prevMsg 才会不为null
                    if (prevMsg != null) {
                        // 重新吧prevMsg放回到链表中
                        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;
            }

            // 如果等待结束后 如果 已经在退出了,那么就不需要再进行处理
            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.
            if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) {
                pendingIdleHandlerCount = mIdleHandlers.size();
            }
            // 如果 没有IdleHandler 那么线程就需要等待
            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)];
            }
            mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
        }

        // IdleHandler 回调,以及是否 需要keep,如果不需要就remove掉
        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);
                }
            }
        }

    }
}

Looper

Looper 是Handler消息处理的泵站,他的主要作用是不断的通过MessageQueue中取出消息,并交个Handler来处理消息。Looper是通过ThreadLocal来进行线程切换的。

初始

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");
    }
    // 创建了一个Looper对象放入到ThreadLocal
    sThreadLocal.set(new Looper(quitAllowed));
}

开始循环

public static void loop() {
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;
    ......
    ......

    for (;;) {
        // 通过MessageQueue的next()方法来获取Message
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }

        ......
        ......
        try {
            // 把消息丢给 Handler.dispatchMessage 处理消息
            msg.target.dispatchMessage(msg);
            if (observer != null) {
                observer.messageDispatched(token, msg);
            }
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } catch (Exception exception) {
            ......
            throw exception;
        } finally {
            ......
        }
        ......
        ......
        // 消息进行回收并入缓存
        msg.recycleUnchecked();
    }
}