Handler机制分析

Handler机制分析

1. Handler机制原理

Android系统中Java应用程序是靠消息驱动来工作的,消息驱动工作原理如下:

  1. 存在一个消息队列,事件源可以往消息队列中投递消息。

  2. 存在一个消息循环,不断从消息队列中取出消息进行处理。

事件源可以是按键、触摸等物理事件也可以是系统或者应用本身发出的请求消息

工作过程如下:

从图中可以看出:

  1. 事件源把待处理的消息加入到消息队列中,一般是加入到队尾,一些优先级高的消息也可以加到队头。

  2. 处理线程不断从队头取出消息并处理。

那么Android是如何实现的呢?

Handler机制就是具体实现,上述工作就是由HandlerLooper实现:

  1. Handler类作为辅助类,封装了消息投递和消息处理接口。

  2. Looper类,用于封装消息循环,并且有一个消息队列(Message队列,里面存储待处理的Message)。

2. Handler机制部分源码分析

a. Looper类分析

首先分析Looper类,我们通过Android程序入口主线程初始化Looper过程开始分析Looper类。

----> ActivityThread.java

public static void main(String[] args) {
    ...
    //1. 调用初始化Looper
    Looper.prepareMainLooper();
    ...
    //2. 进入消息循环
    Looper.loop();
    ...
}

这里有2个关键调用来初始化主线程Looper,我们先分析1处调用。

----> Looper.java

第一个调用是Looper.prepareMainLooper()

public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}
// 一个线程只能调用一次
private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    // 创建一个Looper对象设置到调用线程的局部变量中
    sThreadLocal.set(new Looper(quitAllowed));
}

关键函数prepare会在调用的线程的局部变量中设置一个Looper对象,这个Looper对象就保存在这个调用线程的TLV中,就是通过ThreadLocal机制巧妙的把Looper和调用线程关联在一起。

这里可以看到一个线程只能调用一次prepare函数给自己设置一个Looper对象,所以每个线程只能有一个Looper对象。

第二个调用是Looper.loop()

public static void loop() {
    final Looper me = myLooper();// myLooper返回保存在调用线程TLV中的Looper对象
    ...
    me.mInLoop = true;
    ...
    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(); // 获取一个队列头消息
    ...
    try {
        // 处理消息,Message对象中有一个target,它是Handler类型
        // 调用该消息的Handler,交给他的dispatchMessage函数处理
        msg.target.dispatchMessage(msg);
        ...
    } catch (Exception exception) {
        ...
    } finally {
        ...
    }
    msg.recycleUnchecked();
}

上述代码分析loop的作用:开启一个死循环读取线程TLV中Lopper对象的消息队列并处理。

通过上述分析可以得到:

  1. Looper的prepare函数会将Looper对象和调用线程(也是最终处理线程)绑定。

  2. 处理线程调用loop函数,处理Looper对象消息队列的消息。

当事件源向这个Looper发送消息的时候,就是把消息加到这个Looper对象的消息队列中,之后该消息将由和Looper绑定的处理线程来处理。

事件源何如把消息添加到处理线程的Looper对象的消息队列中呢?

b. Hander类分析

----> Handler.java

先看下代码中Handler是如何使用的,应该都不陌生下面这段代码

private Handler mHandler = new Handler(Looper.getMainLooper()){
    @Override
    public void handleMessage(@NonNull Message msg) {
        ...
        super.handleMessage(msg);
    }
};

代码中有2个地方需要注意:

  1. 在new一个Handler对象的时候会传递一个Looper对象,通过分析源码Handler构造函数,即使没有传递一个Looper对象,也会主动通过Looper.myLooper函数获取当前线程TLV中保存的Looper对象。

由此可以知道Handler和Looper对象初始化是有顺序的,Looper对象需要在Handler对象初始化前就初始化完成

  1. 需要重载handleMessage方法,具体处理不同消息。

Handler为什么需要Looper对象呢?用来做什么?

前面原理部分和Looper类分析说过Looper封装了消息循环并且有一个消息队列,如果没有Handler我们如何往Looper中的消息队列中插入消息?三步可以做到:

  1. 调用Looper.myQueue获取到MessageQueue消息队列对象。

  2. 构造一个Message,写入成员变量,特别是target变量。

  3. 调用MessageQueue.enqueueMessage将消息插入到消息队列

可以看到自己实现这些操作很麻烦,Handler的出现就是为了简化这些操作,它需要Looper对象的原因是因为它帮我们做了上述复杂操作,Handler提供一系列API帮我们完成创建消息和插入消息队列的工作,例如常用的:

// 查看消息队列中是否有消息码为what的消息
public final boolean hasMessages(int what)
// 从Handler中创建Message,涉及到复用不展开,有兴趣看下源码
public final Message obtainMessage()
// 从消息队列中移除消息码为what的消息
public final void removeMessages(int what) 
// 发送消息
public final boolean sendMessage(@NonNull Message msg) 

分析下sendMessage做了什么

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) {
    // mQueue就是Looper中的消息队列
    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);
}

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
    // 把target设置成自己
    msg.target = this; 
    msg.workSourceUid = ThreadLocalWorkSource.getUid();

    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    // 将消息入队列
    return queue.enqueueMessage(msg, uptimeMillis);
}

可以看到进入一些调用后Handler把Message中的target设置为自己,然后加入到消息队列中。

上面分析了Handler消息投递的代码,那么消息处理是什么样的呢?

分析Looper类的时候在loop方法中死循环获取消息处理时有一个调用msg.target.dispatchMessage(msg),没错是调用Handler.dispatchMessage来处理消息。

public void dispatchMessage(@NonNull Message msg) {
    // 如果Message本身有callback,则交给callback来处理
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        // 如果Handler设置了callback,则交给callback来处理
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        // 交给子类来处理
        handleMessage(msg);
    }
}

可以看到dispatchMessage定义了一套消息处理的优先级机制:

  1. 如果消息自带callback处理,则交给callback处理。

  2. 如果Handler设置了全局mCallback处理,则交给mCallback处理。

  3. 如果1,2都没有设置则交给子类实现的handleMessage处理,也就是上面例子中初始化Handler需要重载的函数。

至此Handler机制就说完了

内容来自网络和书本总结,如果侵权留言联系删除。

posted @ 2023-09-06 19:54  羊村灰太狼  阅读(245)  评论(0)    收藏  举报