Handler机制笔记

该文章是一篇通过阅读Handler源码解析Handler消息机制的学习笔记。重点分析了消息循环、消息分发和消息发送的原理

创建

Handler消息机制的Java层核心服务的创建入口位于SystemServer.java中。其通过main函数中的run()方法中实现创建;而在应用中,当应用程序启动后会进入ActivityThread.java中的main函数。其主线程Handler在这里进行初始化。双方核心代码均如下所示

Looper.prepareMainLooper(); //初始化
Looper.loop();  //开始循环

在主线程Looper初始化后,loop方法中无限循环读取新消息,并分发给相应处理者

Looper初始化

Handler消息机制通过ThreadLocal实现线程区分。其核心代码位于Looper.prepare(boolean quitAllowed)中。其中quitAllowed若为true,则代表该Handler为主线程handler。

主线程Looper准备方法

/**
 * 官方不推荐直接调用该方法。其应当有Android环境进行初始化
 */
public static void prepareMainLooper() {
    prepare(false);
    synchronized(Looper.class) {
        if (sMainLooper != null) { //判断主线程是否已存在运转的Looper。若存在则抛错
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper
    }
}

该方法直接调用Looper的静态方法prepare(),以当前线程为主线程。并同步判断是否重复调用。若非重调用则将该looper记录在静态变量中作为主Looper

Looper实际准备方法

...
static final threadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
...

private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {   //判断Looper是否已存在于内存中,若是则抛错
        throw new RuntimeException("Only one Looper may be create per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));
}

在Looper的prepare(boolean quitAllowed)方法中,首先通过静态变量sThreadLocal尝试获取当前Thread中的Looper,以防止重复创建。然后通过构造方法创建Looper并将其存入sThreadLocal中。

Looper构造方法

...
final MessageQueue mQueue;
final Thread mThread;
...

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

在构造方法中对消息队列和其相应的thread变量进行赋值

MessageQueue初始化

...
private long mPtr; // used by native code
...

MessageQueue(boolean quitAllowed) {
    mQuitAllowed = quitAllowed;
    mPtr = nativeInit();
}

MessageQueue的初始化记录了允许退出的标记,同时调用native方法进行进一步初始化。至此Looper初始化完成

Looper.loop()

...
private boolean mInLoop;    // 判断是否在无限循环的标志位
// If set, the looper will show a warning log if a message delivery (actual delivery time - post time) takes longer than this.
private long mSlowDeliveryThresholdMs;
// True if a message delivery takes longer than mSlowDeliveryThresholdMs.
private boolean mSlowDeliveryDetected;  
...

public static void loop() {
    final Looper me = myLooper();   // 就是sThreadLocal.get()
    if (me == null) { // 判空处理
        throw new RuntimeException("no Looper; Looper.prepare() wasn't called on this thread.");
    }
    if (me.mInLoop) {   // 判断是否已经在loop,若已在loop则发出警告log
        Slog.w(TAG, "Loop again would have the queued messages be excuted 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.
    // ↑获取token定位线程
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();
    
    // Allow overiding a threshold with a system prop. e.g.
    // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
    // ↑允许覆盖策略
    final int thresholdOverride = 
        SystemProperties.getInt("log.looger."
            + Process.myUid() + "."
            + Thread.currentThread().getName()
            + ".slow", 0);
            
    me.mSlowDeliveryDetected = false;   // 日志相关,于机制不重要
    
    for (;;) {
        // 死循环
        if (!loopOnce(me, ident, thresholdOverride)) {
            return;
        }
    }
}

loop()方法中主要进行 一些日志和判断工作。最后进入死循环。循环体在方法loopOnce

Looper.loopOnce()

...
private static Observer sObserver;  // 观察者,通过setObserver方法赋值
...

// Poll and deliver single message, return true if the outer loop should continue.
private static boolean loopOnce(final looper me, final long ident, final int                     thresholdOverride) {
    Message = me.mQueue.next(); // might block  // 从队列中获取一个消息
    if (msg == null) {
        // No message indicates that the message queue is quitting.
        // 队列空了,说明队列不再等待新消息。说明队列已回收
        return false;
    }
    
    // 日志相关
    ...
    ...
    
    // 核心代码
    try {
        msg.target.dispatchMessage(msg);    // 发送并处理消息
    } catch (Exception exception) {
        throw exception;
    } finally {
        // 日志
        ...
    }
    
    // 日志相关
    ...
    ...
    
    msg.recycleUnchecked(); // 回收该消息
    
    return true;
}

该方法核心内容为

Message = me.mQueue.next();
msg.target.dispatchMessage(msg);

两句,通过next()方法从消息队列中获取消息,再通过消息的target处理该消息。若流程没有问题则返回true进行下一轮循环。或者block在next()处。

消息队列循环

Handler消息机制的消息存放于MessageQueue中。队列为单链表结构。其维护也是Handler机制的核心

其核心方法为next()

Message next() {
    // 聚焦核心功能代码,去除日志等内容
    ...
    
    int nextPollTimeoutMillis = 0;
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
        nativePollOnce(ptr, nextPolltimeoutMillis); // 一个native方法。若当前消息队列中有消息则获得,否则等待
        synchronized(this) {
            // Try to retrieve the next message. Return if found.
            final long now = SystemClock.uptimeMillis();    // 获取系统时钟时间
            Message prevMsg = null; // 上一消息
            Message msg = mMessage; // 当前消息
            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 read. 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 {
                        mMessage = msg.next;    // 锁定信息
                    }
                    msg.next = null;
                    msg.markInUse();
                    return msg;
                }
            } else {
                // No more message.
                nextPollTimeoutMillis = -1;
            }
            
            // Process the quit message now that all pending messages have been handled.
            if (mQuitting) {
                dispose();
                return null
            }
            
            ...
        }
    }
}

next()方法通过for循环不断获取msg,其中nativePollOnce()是一个native方法,它会尝试获取消息队列中是否有消息,若有则获得,否则等待。获取到msg后,首先判断其是否存在target,若其为空,说明该msg不包含任何操作,没有处理对象。它会尝试遍历消息链表获取下一有效msg。

之后判断msg的执行时间(when),若该时间小于循环体最初获取的当前时间(now),则等待至该执行时间。若执行时间大于等于当前时间,则将该msg返回到Looper的loop()方法中,并整理链表。接下来会通过dispatchMessage()方法将消息分发至实际的handler中。

消息入队(发送)

在handler的使用中,我们通过handler.sendMessage(msg)方法发送消息。

// android.os.Handler

final MessageQueue mQueue;  // 消息队列实例

public final boolean sendMessage(Message msg) {
    return sendMessageDelayed(msg, 0);
}

public final boolean sendmessageDelayed(Message msg, long delayMills) {
    if (delayMills < 0) {
        delayMills = 0; // 处理下边界
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMills);
}

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

通过上述代码可知,最终sendmessage()方法会进入enqueueMessage()

// android.os.Handler

private boolean enqueuemessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    msg.workSourceUid = ThreadLocalWorkSource.getUid();
    
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    
    return queue.enqueueMessage(msg, uptimemillis);
}

该方法首先将自己赋值到msg的target属性上,接下来调用了MessageQueue的enqueueMessage()方法。

// android.os.MessageQueue

boolean enqueueMessage(Message msg, long when) {
    if (msg.target == null) {
        // 判断target是否为空,若为空则抛错
        throw new IllegalArgumentException("Message must have a target.");
    }
    
    synchronized(this) {
        ... // 省略一些判断
        
        msg.when = when;
        Message p = mMessages;  // 链表指针指向的msg,当前指向消息队列队首
        boolean needWake;
        
        if (p == null || when == 0 || when < p.when) {
            // 消息队列为空、when为0(立刻执行)、when小于队首when(插入队首)
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;
        } else {
            // 其它情况,需要根据when选择msg插入位置
            needWake = mBlock && p.target == null && msg.isAsynchronous();
            message prev;   // 链表前序变量
            for (;;) {
                prev = p;
                p = p.next; 
                if (p == null || when < p.when) {
                    // 已至队尾或执行时间小于指针msg的执行时间,则插入至指针之前
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            
            // 执行链表插入
            msg.next = p;
            prev.next = msg;
            
            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
    }
}

如上述代码所示,核心逻辑就是通过待插入msg的when,遍历链表寻找合适的位置插入。之后msg便等待Looper.loop()方法的循环在合适的时机被取出执行了。至此Handler消息循环机制完成

总结

在系统启动、应用启动时,会首先激活主线程的Looper。每个线程最多只能存在一个活跃的Looper,保存在静态遍历ThreadLocal中,且该Looper必须调用Looper.loop()方法。
Looper.loop()方法中,通过死循环不断的从保存在Looper中的MessageQueue中获取消息,并分发到目标Handler中执行。
MessageQueue中维护着一个单链表,链表通过Message的when属性进行排序。当发送新消息时则执行链表插入。直到该msg被loop循环选中出队。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐