Handler三部曲(1)Handler源代码详细注解

网上关于Handler原理和源码的分析已经足够多,这篇文章只会对原理稍加提及,不会多做解释。更多的是对源码注释和API的介绍,希望能给读者一些启发。

1.Handler原理简介

Android异步消息处理机制完全解析,带你从源码的角度彻底理解_郭霖 android异步消息处理机制完全解-CSDN博客

郭大佬这篇文章有点久了,源代码也与新版本的有所不同,不过不影响整体思想和流程的理解。

Handler发送消息到MessageQueue中,Looper从MessageQueue中循环获取消息并处理。

Android API32中Handler源码长度1001行,其实里面内容也很简单,无非就是:构造方法 以及对消息的。没有太多难点,不过众多的post和send方法之间的调用关系需要捋一捋。

2.类注释解读

Handler是干嘛用的?有哪些能力?用的时候要注意啥?

这些在Handler源码注释里讲地明明白。

我会给出源码注释每一段的直译,再下方做出一些自己的理解。如果嫌看着麻烦,可以略过直译部分。

A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler it is bound to a Looper. It will deliver messages and runnables to that Looper's message queue and execute them on that Looper's thread.

Handler可以发送和处理MessageQueue中的Message和Runable对象,MessageQueue是与线程绑定的。

每个 Handler 实例都会和单个线程和这个线程的消息队列进行绑定。

当创建新的 Handler 时,它将绑定到 Looper。它将消息和可运行对象传递到该 Looper 的消息队列,并在该 Looper 的线程上执行它们。

意思就是Handler可以发送和处理Message和Runable对象,Handler在创建的时候要绑定一个Looper,这个Looper和存放消息的MessageQueue都是跟线程绑定的。

There are two main uses for a Handler: (1) to schedule messages and runnables to be executed at some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.

Handler 有两个主要用途:(1) 将消息和可运行对象安排在将来的某个时间点执行;(2) 向其他线程的消息队列发送需要执行的消息。

用Handler发延时消息,可以指定这个消息处理的时间。这个Handler不是绑定了Looper和MessageQueue嘛,你发的消息最后都会在Looper在的线程上跑,那你在 A线程用绑定了 B线程的Handler发消息,不就实现了跨线程通信嘛。

Scheduling messages is accomplished with the post, postAtTime(Runnable, long), postDelayed, sendEmptyMessage, sendMessage, sendMessageAtTime, and sendMessageDelayed methods. The post versions allow you to enqueue Runnable objects to be called by the message queue when they are received; the sendMessage versions allow you to enqueue a Message object containing a bundle of data that will be processed by the Handler's handleMessage method (requiring that you implement a subclass of Handler).

计划消息是通过 post、postAtTime(Runnable, long)、postDelayed、sendEmptyMessage、sendMessage、sendMessageAtTime 和 sendMessageDelayed 方法完成的。post 方法允许您将 Runnable 对象加入队列,以便在收到消息队列时调用它们;sendMessage 版本允许您将包含将由 Handler 的 handleMessage 方法处理的数据包的 Message 对象加入队列(要求您实现 Handler 的子类)。

可以用Handler的 post开头的各种方法发送一个Runnable对象,也可以使用 send开头的各种方法发送要处理的Message。你要是用sendMessage去发送Message消息,那你就要实现一个Handler的子类,重写一下handleMessage方法,在handleMessage里面去处理Message消息。

When posting or sending to a Handler, you can either allow the item to be processed as soon as the message queue is ready to do so, or specify a delay before it gets processed or absolute time for it to be processed. The latter two allow you to implement timeouts, ticks, and other timing-based behavior.

在向Handler发送要处理的消息时,你可以指定在消息队列准备好后立即处理该项目,也可以指定处理消息的延迟时间或处理消息的绝对时间。可以用后两者来实现超时、周期和其他基于计时的行为。

用Handler给消息队列里发送消息可以给消息指定个时间(延迟时间或者执行的精确时间(SystemClock时间)),当然了也可以不指定时间,让消息队列按顺序去处理。既然Handler可以给消息指定执行时间,我们就能拿Handler去做一些超时、轮询之类的操作。

When a process is created for your application, its main thread is dedicated to running a message queue that takes care of managing the top-level application objects (activities, broadcast receivers, etc) and any windows they create. You can create your own threads, and communicate back with the main application thread through a Handler. This is done by calling the same post or sendMessage methods as before, but from your new thread. The given Runnable or Message will then be scheduled in the Handler's message queue and processed when appropriate.

为应用程序创建进程时,其主线程专用于运行消息队列,该消息队列负责管理顶级应用程序对象(活动、广播接收器等)及其创建的任何窗口。你可以创建自己的线程,并通过 Handler 与主应用程序线程进行通信。跟以前一样,也是通过调用 post 或 sendMessage 方法来完成的,只是说这些请求但来自你的新线程。然后,给定的 Runnable 或 Message 将被安排在处理程序的消息队列中,并在适当的时候进行处理。

App启动的时候,会创建应用程序的线程,并且会创建一个绑定了主线程Looper和MessageQueue,系统在管理Activity、广播或者其他系统级别的对象的时候,就会给主线程消息队列来处理,我觉得这里指的是ActivityThread里面的类名为H的Handler的子类吧。

跨进程通信,其实就是两个进程进行交互。用Handler咋实现呢,比如说有个Handler跟主线程绑定了,假设名字叫mainHandler,我们跑一个子线程,在子线程里巴拉巴拉处理了一堆事情,然后拿着mainHandler去发个消息通知下,这个发出的消息最后就在主线里处理了嘛,这就是所谓的跨进程通信。

kotlin 复制代码
class Test{
    private val mainHandler = Handler(Looper.getMainLooper())

    fun test() {
       thread {
            // 子线程里balabala
            mainHandler.post {
                // 我在主线程里被调用啦
            }
        }.start() 
    }
}

3.成员变量及注释

java 复制代码
    /*
     * Set this flag to true to detect anonymous, local or member classes
     * that extend this Handler class and that are not static. These kind
     * of classes can potentially create leaks.
     * 将此标志设置为 true 可检测扩展此 Handler 类且非静态的匿名类、本地类或成员类。
     * 这类可能会造成泄漏。
     */
    private static final boolean FIND_POTENTIAL_LEAKS = false;
    private static final String TAG = "Handler";
    // 绑定主线程Looper的Handler
    private static Handler MAIN_THREAD_HANDLER = null;

    final Looper mLooper;
    final MessageQueue mQueue;
    final Callback mCallback;
    // 是否是异步handler的标志
    final boolean mAsynchronous;
    IMessenger mMessenger;

这里有个静态变量叫做FIND_POTENTIAL_LEAKS,应该是开发者担心出现内存泄漏的情况来调试用的。这个变量在Handler的一个hide的构造方法里有用到,当FIND_POTENTIAL_LEAKS为true时,如果这个handler是非静态的匿名类、内部类或成员类,就打印一个log提醒。

ini 复制代码
    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;
    }

4.类函数注释和分析

4.1 构造方法

Handler有多个构造方法,所有构造方法都会最后调用到 Handler(Callback callback, boolean async) 和

Handler(Looper looper, Callback callback, boolean async)。

这两个方法内部差距并不大,内部都是设置Looper、设置Callback、设置是否异步消息async。但调用 Handler(Callback callback, boolean async)的构造函数不是hide就是被标记为废弃的方法,所以,我们常用的构造方法是Handler(Looper looper)和Handler(Looper looper, Callback callback) ,参数 async都是false。

可以看出,设计者想让我们使用Handler的时候,直接在构造函数里面指定Looper。

4.2 静态构造异步Handler函数

就是调用构造方法返回一个Handler。从这里可以看出,设计者并不想让我们直接创建一个异步Handler,而是通过createAsync方法,以确保looper或者callback不为空。

less 复制代码
    public static Handler createAsync(@NonNull Looper looper, @NonNull Callback callback) {
        if (looper == null) throw new NullPointerException("looper must not be null");
        if (callback == null) throw new NullPointerException("callback must not be null");
        return new Handler(looper, callback, true);
    }

        public static Handler createAsync(@NonNull Looper looper) {
        if (looper == null) throw new NullPointerException("looper must not be null");
        return new Handler(looper, null, true);
    }

4.3 获取mainThreadHandler

是hide方法,这个MAIN_THREAD_HANDLER应该是留给系统使用的。就算没有这两个方法,我们也可以很方便地创建一个mainThreadHandler对吧:new Handler(Looper.getMainLooper())

typescript 复制代码
/** @hide */
public static Handler getMain() {
        if (MAIN_THREAD_HANDLER == null) {
            MAIN_THREAD_HANDLER = new Handler(Looper.getMainLooper());
        }
        return MAIN_THREAD_HANDLER;
    }

    /** @hide */
    public static Handler mainIfNull(@Nullable Handler handler) {
        return handler == null ? getMain() : handler;
    }

4.4 获取消息名字

根据一定规则拼接出一个Message的名字,返回String。

less 复制代码
    /** {@hide} */
    public String getTraceName(@NonNull Message message) {
        if (message.callback instanceof TraceNameSupplier) {
            return ((TraceNameSupplier) message.callback).getTraceName();
        }

        final StringBuilder sb = new StringBuilder();
        sb.append(getClass().getName()).append(": ");
        if (message.callback != null) {
            sb.append(message.callback.getClass().getName());
        } else {
            sb.append("#").append(message.what);
        }
        return sb.toString();
    }

        public String getMessageName(@NonNull Message message) {
        if (message.callback != null) {
            return message.callback.getClass().getName();
        }
        return "0x" + Integer.toHexString(message.what);
    }

4.5从线程池获取一个消息

有五个

另外有两个私有方法

这些方法都会返回一个Message,为什么要使用obtain方法获取Message呢?主要是Message中有个静态变量(sPoolSync)维护了一个Message的线程池,线程池会把已经用过了的Message回收,线程池最大50个。

我们如果多次去发送Message消息,不断的创建新对象,势必会造成内存抖动,使用消息池实现了Message的复用。

java 复制代码
public final Message obtainMessage()
{
    return Message.obtain(this);
}

public final Message obtainMessage(int what)
{
    return Message.obtain(this, what);
}

public final Message obtainMessage(int what, @Nullable Object obj) {
    return Message.obtain(this, what, obj);
}

public final Message obtainMessage(int what, int arg1, int arg2)
{
    return Message.obtain(this, what, arg1, arg2);
}

public final Message obtainMessage(int what, int arg1, int arg2, @Nullable Object obj) {
    return Message.obtain(this, what, arg1, arg2, obj);
}

private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}

@UnsupportedAppUsage
private static Message getPostMessage(Runnable r, Object token) {
    Message m = Message.obtain();
    m.obj = token;
    m.callback = r;
    return m;
}

分别调用了Message的不同参数的的obtain方法。

4.6 post消息方法

众多的post方法,给了用户很多的使用空间。除了postAtFrontOfQueue,其他的最终都会调用到sendMessageAtTime这个方法。

使用post发送消息时,会发送一个Runnable对象,这个Runnable对象会附着在一个新的Message对象上,最终还是以Message的形式发送到消息队列上。

这里还有个入参是uptimeMillis,这便是消息要执行的精确时间。将消息放入到消息队列的时候,消息队列会按照这个时间进行排序,执行时间早的在前,执行时间晚的在后。

4.7 runWithScissors

hide方法。同步运行指定的任务。

java 复制代码
    public final boolean runWithScissors(@NonNull Runnable r, long timeout) {
        // 省略部分
        if (Looper.myLooper() == mLooper) {
            r.run();
            return true;
        }

        BlockingRunnable br = new BlockingRunnable(r);
        return br.postAndWait(this, timeout);
    }

如果当前线程与处理程序线程(即Looper线程)相同,则立即运行。否则,将可运行对象发送到处理程序并等待完成,然后再返回。

有个场景可能会用到这个方法: 当刚刚设置一个 Handler 线程,并且需要在继续执行之前做一些初始化动作。

如果是延时消息,postAndWait内部会先用当前Handler去post发送Runnable消息,调用wait方法将线程挂起一段时间,当消息运行结束,会调用notifyAll恢复线程。

使用此方法时,要在退出 looper 时使用 Looper.quitSafely。不然,runWithScissors 可能会无限期挂起。

4.8 send消息

可以看到,使用的send发送Message时,最终还是会调用到sendMessageAtTime这个方法。

想要发送延迟消息,可以用延时时间为入参的sendMessageDelayed,也可以使用精确时间为入参的sendMessageAtTime。

需要注意的是这里的精确时间是SystemClock获取到的时间(设备开机开始进行计时的时间)。

另外讲解一个特殊的send方法,也是个hide方法:

less 复制代码
public final boolean executeOrSendMessage(@NonNull Message msg) {
        if (mLooper == Looper.myLooper()) {
            dispatchMessage(msg);
            return true;
        }
        return sendMessage(msg);
    }

如果在此处理程序对应的同一线程上调用消息,则同步执行消息,否则sendMessage 将其推送到队列。

4.9 enqueueMessage

less 复制代码
    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);
    }

不管是pos还是send,所有发送消息的方法都会调用到这个方法,这是个私有方法,设置msg的target为当前handler,设置workSourceUid(消息源的uid,这个只是系统内部使用),设置是否是同步消息。

4.10 remove消息

根据入参作为判断条件,移除消息列表中的消息。

逻辑比较简单,不做深究。

less 复制代码
    public final void removeCallbacks(@NonNull Runnable r) {
        mQueue.removeMessages(this, r, null);
    }

    /**
     * Remove any pending posts of Runnable <var>r</var> with Object
     * <var>token</var> that are in the message queue.  If <var>token</var> is null,
     * all callbacks will be removed.
     */
    public final void removeCallbacks(@NonNull Runnable r, @Nullable Object token) {
        mQueue.removeMessages(this, r, token);
    }

        public final void removeCallbacksAndMessages(@Nullable Object token) {
        mQueue.removeCallbacksAndMessages(this, token);
    }

以我们最常用的removeCallbacksAndMessages为例,删除 obj 为 token 的任何待处理的消息。如果 token 为 null,则将删除所有回调和消息。

我们经常调用removeCallbacksAndMessages(null)来删除handler中所有的消息。

4.11 验证是否存在消息

按照查询条件,查找消息队列中是不是存在消息。

4.12 getLooper

arduino 复制代码
    public final Looper getLooper() {
        return mLooper;
    }

4.13 打印消息

less 复制代码
    public final void dump(@NonNull Printer pw, @NonNull String prefix) {
        pw.println(prefix + this + " @ " + SystemClock.uptimeMillis());
        if (mLooper == null) {
            pw.println(prefix + "looper uninitialized");
        } else {
            mLooper.dump(pw, prefix + "  ");
        }
    }

    /**
     * @hide
     */
    public final void dumpMine(@NonNull Printer pw, @NonNull String prefix) {
        pw.println(prefix + this + " @ " + SystemClock.uptimeMillis());
        if (mLooper == null) {
            pw.println(prefix + "looper uninitialized");
        } else {
            mLooper.dump(pw, prefix + "  ", this);
        }
    }

        @Override
    public String toString() {
        return "Handler (" + getClass().getName() + ") {"
        + Integer.toHexString(System.identityHashCode(this))
        + "}";
    }

hide方法。将消息队列中的所有消息打印出来,会调用到MessageQueue.dump,这是个同步方法。

4.14 跨进程通信

Messenger实现跨进程通信的关键方法。被@UnsupportedAppUsage注解,app内无法使用。

scala 复制代码
    final IMessenger getIMessenger() {
        synchronized (mQueue) {
            if (mMessenger != null) {
                return mMessenger;
            }
            mMessenger = new MessengerImpl();
            return mMessenger;
        }
    }

    private final class MessengerImpl extends IMessenger.Stub {
        public void send(Message msg) {
            msg.sendingUid = Binder.getCallingUid();
            Handler.this.sendMessage(msg);
        }
    }
相关推荐
众拾达人2 小时前
Android自动化测试实战 Java篇 主流工具 框架 脚本
android·java·开发语言
吃着火锅x唱着歌3 小时前
PHP7内核剖析 学习笔记 第四章 内存管理(1)
android·笔记·学习
_Shirley5 小时前
鸿蒙设置app更新跳转华为市场
android·华为·kotlin·harmonyos·鸿蒙
hedalei6 小时前
RK3576 Android14编译OTA包提示java.lang.UnsupportedClassVersionError问题
android·android14·rk3576
锋风Fengfeng6 小时前
安卓多渠道apk配置不同签名
android
枫_feng7 小时前
AOSP开发环境配置
android·安卓
叶羽西7 小时前
Android Studio打开一个外部的Android app程序
android·ide·android studio
qq_171538859 小时前
利用Spring Cloud Gateway Predicate优化微服务路由策略
android·javascript·微服务
Vincent(朱志强)10 小时前
设计模式详解(十二):单例模式——Singleton
android·单例模式·设计模式
mmsx11 小时前
android 登录界面编写
android·登录界面