设为首页 加入收藏

TOP

深入理解 Handler 消息机制(一)
2019-09-06 00:27:03 】 浏览:142
Tags:深入 理解 Handler 消息 机制

记得很多年前的一次面试中,面试官问了这么一个问题,你在项目中一般如何实现线程切换? 他的本意应该是考察 RxJava 的使用,只是我的答案是 Handler,他也就没有再追问下去了。在早期 Android 开发的荒芜时代,Handler 的确承担了项目中大部分的线程切换工作,通常包括子线程更新 UI 和消息传递。不光在我们自己的应用中,在整个 Android 体系中,Handler 消息机制也是极其重要的,不亚于 Binder 的地位。 ActivityThread.java 中的内部类 H 就是一个 Handler,它内部定义了几十种消息类型来处理一些系统事件。

Handler 的重要性毋庸置疑,今天就通过 AOSP 源码来深入学习 Handler。相关类的源码包含注释均已上传到我的 Github 仓库 android_9.0.0_r45 :

Handler.java

Looper.java

Message.java

MessageQueue.java

Handler

Handler 用来发送和处理线程对应的消息队列 MessageQueue 中存储的 Message。每个 Handler 实例对应一个线程以及该线程的消息队列。当你创建一个新的 Handler,它会绑定创建它的线程和消息队列,然后它会向消息队列发送 Message 或者 Runnable,并且在它们离开消息队列时执行。

Handler 有两个主要用途:

  1. 规划 Message 或者 Runnable 在未来的某个时间点执行
  2. 在另一个线程上执行代码

以上翻译自官方注释。说白了,Handler 只是安卓提供给开发者用来发送和处理事件的,而消息如何存储,消息如何循环取出,这些逻辑则交给 MessageQueueLooper 来处理,使用者并不需要关心。但要真正了解 Handler 消息机制,认真读一遍源码就必不可少了。

构造函数

Handler 的构造函数大致上可以分为两类,先来看第一类:

public Handler() {
    this(null, false);
}

public Handler(Callback callback) {
    this(callback, false);
}

public Handler(Callback callback, boolean async) {
    // 如果是匿名类、内部类、本地类,且没有使用 static 修饰符,提示可能导致内存泄漏
    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());
        }
    }

    // 从当前线程的 ThreadLocal获取 Looper
    mLooper = Looper.myLooper();
    if (mLooper == null) {  // 创建 Handler 之前一定要先创建 Looper。主线程已经自动为我们创建。
        throw new RuntimeException(
            "Can't create handler inside thread " + Thread.currentThread()
                    + " that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue; // Looper 持有一个 MessageQueue
    mCallback = callback; // handleMessage 回调
    mAsynchronous = async; // 是否异步处理
}

这一类构造函数最终调用的都是两个参数的方法,参数中不传递 Looper,所以要显式检查是否已经创建 Looper。创建 Handler 之前一定要先创建 Looper,否则会直接抛出异常。在主线程中 Looper 已经自动创建好,无需我们手动创建,在 ActivityThread.javamain() 方法中可以看到。Looper 持有一个消息队列 MessageQueue,并赋值给 Handler 中的 mQueue 变量。Callback 是一个接口,定义如下:

public interface Callback {
    public boolean handleMessage(Message msg);
}

通过构造器参数传入 CallBack 也是 Handler 处理消息的一种实现方式。

再回头看一下在上面的构造函数中是如何获取当前线程的 Looper 的?

 mLooper = Looper.myLooper(); // 获取当前线程的 Looper

这里先记着,回头看到 Looper 源码时再详细解析。

看过 Handler 的第一类构造函数,第二类其实就很简单了,只是多了 Looper 参数而已:

public Handler(Looper looper) {
    this(looper, null, false);
}
    
public Handler(Looper looper, Callback callback) {
    this(looper, callback, false);
}
    
public Handler(Looper looper, Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

直接赋值即可。

除此之外还有几个标记为 @hide 的构造函数就不作说明了。

发送消息

发送消息大家最熟悉的方法就是 sendMessage(Message msg) 了,可能有人不知道其实还有 post(Runnable r) 方法。虽然方法名称不一样,但最后调用的都是同一个方法。

sendMessage(Message msg)
sendEmptyMessage(int what)
sendEmptyMessageDelayed(int what, long delayMillis)
sendEmptyMessageAtTime(int what, long uptimeMillis)
sendMessageAtTime(Message msg, long uptimeMillis)

几乎所有的 sendXXX() 最后调用的都是 sendMessageAtTime() 方法。

post(Runnable r)
postAtTime(Runnable r, long uptimeMillis)
postAtTime(Runnable r, Object token, long uptimeMillis)
postDelayed(Runnable r, long delayMillis)
postDelayed(Runnable r, Object token, long delayMillis)

所有的 pos

首页 上一页 1 2 3 4 5 下一页 尾页 1/5/5
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇基于wanAndroid-项目实战 下一篇Android几种多渠道打包

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目