之前写了一篇关于 Message 的文章,感兴趣的朋友可以去看一下【Android 消息机制之 Message 解析(面试)】,这一次咱们来聊一下消息机制中用得最多的 Handler,也是面试中问得最多的之一,在这里我先抛几个问题出来:
我们带着问题去看源码:
大家先对一下的对象,脑补一下工厂的情景:
Handler: 消息的处理者,工厂中流水线的工人。
Message: 系统传递的消息,工厂中流水线上的产品。
MessageQueue: 消息队列,工厂中流水线上的传送带。
Looper: 发动机,工厂中使流水线的传送带运动的发动机。
我们先从程序入口来进行分析,Android 应用程序的入口在 ActivityThread 的 main 函数中,我们先从 main 函数进行分析:
ActivityThread.java:
- public static void main(String[] args) {
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
- SamplingProfilerIntegration.start();
- // CloseGuard defaults to true and can be quite spammy. We
- // disable it here, but selectively enable it later (via
- // StrictMode) on debug builds, but using DropBox, not logs.
- CloseGuard.setEnabled(false);
- Environment.initForCurrentUser();
- // Set the reporter for event logging in libcore
- EventLogger.setReporter(new EventLoggingReporter());
- // Make sure TrustedCertificateStore looks in the right place for CA certificates
- final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
- TrustedCertificateStore.setDefaultUserDirectory(configDir);
- Process.setArgV0("");
- //在android应用程序的入口其实在ActivityThread的main方法
- //在这里,主线程会创建一个Looper对象。
- Looper.prepareMainLooper();
- ActivityThread thread = new ActivityThread();
- thread.attach(false);
- if (sMainThreadHandler == null) {
- sMainThreadHandler = thread.getHandler();
- }
- if (false) {
- Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread"));
- }
- // End of event ActivityThreadMain.
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- //执行消息循环
- Looper.loop();
- throw new RuntimeException("Main thread loop unexpectedly exited");
- }
代码很长,我们挑关键的代码来看,在代码中我已写上注释,在 main 函数中,Looper 调用了 prepareMainLooer(), 我们再进去 Looper 看看。
Looper.java:
- /**
- * Initialize the current thread as a looper, marking it as an
- * application's main looper. The main looper for your application
- * is created by the Android environment, so you should never need
- * to call this function yourself. See also: {@link #prepare()}
- */
- public static void prepareMainLooper() {
- //在主线程中,其默认初始化一个Looper对象,因此我们在主线程的操作中是不需要自己去调prepare()。
- prepare(false);
- synchronized(Looper.class) {
- //这里先进行判断,在主线程是否已经存在Looper了,
- // 避免我们手动去调用prepareMainLooper(),因为这个是给程序入口初始化的时候系统会自动调用的
- if (sMainLooper != null) {
- throw new IllegalStateException("The main Looper has already been prepared.");
- }
- //设置全局变量,主线程的looper
- sMainLooper = myLooper();
- }
- }
Initialize the current thread as a looper, marking it as an* application's main looper. The main looper for your application* is created by the Android environment, so you should never need* to call this function yourself.
注意这个函数的注释,大概意思是:在主线程创建一个 looper,是这个主线程的主 looper,当这个 app 在初始化的时候就会自行创建,因此这个函数不是给你们调用的,是给系统自身在程序创建的时候调用的。
我们继续往下看,有个 prepare(boolean) 函数,我们去看看这个到底是用来干什么的。 Looper.java:
- private static void prepare(boolean quitAllowed) {
- //先判断当前线程是否已经存在Looper了,如果存在,不允许设置新的Looper对象,一个线程只允许存在一个Looper
- if (sThreadLocal.get() != null) {
- throw new RuntimeException("Only one Looper may be created per thread");
- }
- //在当前线程中,创建新的Looper对象,并绑定当前线程
- sThreadLocal.set(new Looper(quitAllowed));
- }
在这里,我们看到了 sThreadLocal, 我们先看看这个 sThreadLocal 在 Looper 是干什么用的。
Looper.java:
- // sThreadLocal.get() will return null unless you've called prepare().
- //sThreadLocal在Looper中作为全局变量,用于保存每个线程中的数据,可以看做是容器
- static final ThreadLocal < Looper > sThreadLocal = new ThreadLocal < Looper > ();
Looper 中,sThreadLocal 作为一个全局变量,sThreadLocal 其实是保存 Looper 的一个容器,我们继续往 ThreadLocal 的 get、set 进行分析。
ThreadLocal.java:
- /**
- * Returns the value in the current thread's copy of this
- * thread-local variable. If the variable has no value for the
- * current thread, it is first initialized to the value returned
- * by an invocation of the {@link #initialValue} method.
- *
- * @return the current thread's value of this thread-local
- */
- public T get() {
- //获取当前线程保存的对象--通过get函数来获取Looper对象
- Thread t = Thread.currentThread();
- ThreadLocalMap map = getMap(t);
- if (map != null) {
- ThreadLocalMap.Entry e = map.getEntry(this);
- if (e != null) return (T) e.value;
- }
- return setInitialValue();
- }
- /**
- * Sets the current thread's copy of this thread-local variable
- * to the specified value. Most subclasses will have no need to
- * override this method, relying solely on the {@link #initialValue}
- * method to set the values of thread-locals.
- *
- * @param value the value to be stored in the current thread's copy of
- * this thread-local.
- */
- public void set(T value) {
- //把当前的looper保存到当前线程中
- Thread t = Thread.currentThread();
- ThreadLocalMap map = getMap(t);
- if (map != null) map.set(this, value);
- else createMap(t, value);
- }
我们看到关键的代码: Thread t=Thread.currentThread();
也就是说,我们的 Looper 对象分别保存在相对应的线程中。我们看回来我们的 prepare(boolean) 函数: looper.java:
- private static void prepare(boolean quitAllowed) {
- //先判断当前线程是否已经存在Looper了,如果存在,不允许设置新的Looper对象,一个线程只允许存在一个Looper
- if (sThreadLocal.get() != null) {
- throw new RuntimeException("Only one Looper may be created per thread");
- }
- //在当前线程中,创建新的Looper对象,并绑定当前线程
- sThreadLocal.set(new Looper(quitAllowed));
- }
Looper.prepare(boolean) 的作用就是创建一个 Looper 对象,并与当前线程绑定在一起。在代码中,首先判断当前线程是否已经存在 looper,如果不存在则创建新的 looper 并且绑定到当前的线程上。
再看回之前的代码: looper.java:
- * **Initialize the current thread as a looper,
- marking it as an * application 's main looper. The main looper for your application
- * is created by the Android environment, so you should never need
- * to call this function yourself. See also: {@link #prepare()}
- */
- public static void prepareMainLooper() {
- //在主线程中,其默认初始化一个Looper对象,因此我们在主线程的操作中是不需要自己去调prepare()。
- prepare(false);
- synchronized (Looper.class) {
- //这里先进行判断,在主线程是否已经存在Looper了,
- // 避免我们手动去调用prepareMainLooper(),因为这个是给程序入口初始化的时候系统会自动调用的
- if (sMainLooper != null) {
- throw new IllegalStateException("The main Looper has already been prepared.");
- }
- //设置全局变量,主线程的looper
- sMainLooper = myLooper();
- }
- }'
分别看一下 sMainLooper 是什么,myLooper() 又是什么? Looper.java:
- //保存一个主线程的looper
- private static Looper sMainLooper; // guarded by Looper.class
- /**
- * Return the Looper object associated with the current thread. Returns
- * null if the calling thread is not associated with a Looper.
- */
- public static Looper myLooper() {
- //使用当前线程的looper
- return sThreadLocal.get();
- }
sMainLooper 在 Looper 做为一个全局变量,保存主线程绑定的 looper,myLooper() 则是获取当前线程绑定的 Looper。在 prepareMainLooper() 中, 在主线程中创建一个新的 Looper, 并且绑定主线程中,同时把这个主线程的 looper 赋值给 sMainLooer 这个全局变量。
ActivityThread.java:
- public static void main(String[] args) {
- ......
- //在android应用程序的入口其实在ActivityThread的main方法
- //在这里,主线程会创建一个Looper对象。
- Looper.prepareMainLooper();
- ......
- ......
- ......
- //执行消息循环
- Looper.loop();
- throw new RuntimeException("Main thread loop unexpectedly exited");
- }
- }
在应用程序 ActivityThread.main 入口中,系统除了调用 Looper.prepareMainLooper,而且在最后还调用了 Looper.loop(), 这个函数有什么?大家脑补一下,工厂里的流水线上,除了有传送带外,如果你不让它动起来,那传送带也没什么作用,那么 Looper.loop 的作用就是让这个传送带动起来,也就是我们的让我们的消息队列动起来。
Looper.java:
- /**
- * 调用此函数用于启动消息队列循环起来,作用相当于工厂流水线中的传送带的开关,
- * 只有把开关打开,传送带才跑起来
- * Run the message queue in this thread. Be sure to call
- * {@link #quit()} to end the loop.
- */
- public static void loop() {
- //先进行判断当前线程是否有绑定looper
- final Looper me = myLooper();
- if (me == null) {
- throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
- }
- //获取这个looper的消息队列
- final MessageQueue queue = me.mQueue;
- // Make sure the identity of this thread is that of the local process,
- // and keep track of what that identity token actually is.
- Binder.clearCallingIdentity();
- final long ident = Binder.clearCallingIdentity();
- //循环通过消息队列来获取消息
- for (;;) {
- Message msg = queue.next(); // might block
- if (msg == null) {
- // No message indicates that the message queue is quitting.
- return;
- }
- // This must be in a local variable, in case a UI event sets the logger
- final Printer logging = me.mLogging;
- if (logging != null) {
- logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what);
- }
- final long traceTag = me.mTraceTag;
- if (traceTag != 0) {
- Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
- }
- try {
- //关键点,这里的msg.target也就是hanlder.看回代码hanlder.enqueueMessage()
- msg.target.dispatchMessage(msg);
- } finally {
- if (traceTag != 0) {
- Trace.traceEnd(traceTag);
- }
- }
- if (logging != null) {
- logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
- }
- // Make sure that during the course of dispatching the
- // identity of the thread wasn't corrupted.
- final long newIdent = Binder.clearCallingIdentity();
- if (ident != newIdent) {
- Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what);
- }
- //最后回收这个message
- msg.recycleUnchecked();
- }
- }
这一段代码比较长,我们挑有中文注释的来看,先判断当前的线程是否存在 looper,如果存在获取保存在 Looper 的消息队列 messagequeue, 然后无限循环这个消息队列来获取 message, 注意我们留到了一段代码:
- //关键点,这里的msg.target也就是hanlder.看回代码hanlder.enqueueMessage()
- msg.target.dispatchMessage(msg);
msg.target 其实就是我们的 handler, 无论是 handler 通过 post 或者 sendEmptyMessage, 最终都会调用到调到这个 enqueueMessage(), 在这里会将 handler 赋值到 msg.target 中.
Handler.java:
- private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
- //在message中放一个标记
- msg.target = this;
- if (mAsynchronous) {
- msg.setAsynchronous(true);
- }
- //在这里把消息放到队列里面去
- return queue.enqueueMessage(msg, uptimeMillis);
- }
既然 Looper 中的 loop() 调用了 msg.target.dispatchMessage,我们就看看 Handler 的 dispatchMessage 是如何进行处理这个 msg 的。
Handler.java:
- /**
- * Handle system messages here.
- */
- public void dispatchMessage(Message msg) {
- //这里先判断callback是否为空
- // callback就是我们使用handler.post(Runnable r)的入参runnable
- if (msg.callback != null) {
- handleCallback(msg);
- } else {
- //如果hanlder的入参callback不为空,优先处理
- if (mCallback != null) {
- //如果回调返回true.则拦截了handler.handleMessage的方法
- if (mCallback.handleMessage(msg)) {
- return;
- }
- }
- //这就是为什么我们使用hanlder的时候,需要重写handleMessage的方法
- handleMessage(msg);
- }
- }
在 dispatchMessage 函数中,意思就是分发这个消息,在代码中先判断 msg.callback 是否为空,msg.callback 是什么?在上一篇文章已详细介绍过了,其实就是 handler.post 中的 runnable 对象,通俗的来说就是 handler 如果有 post 操作的,就处理 post 的操作,我们在看看 handlerCallback 这个函数。 Handler.java:
- private static void handleCallback(Message message) {
- message.callback.run();
- }
很简单,就一行代码,我们看到了熟悉的 run 方法,这个不就是我们使用 post 的时候传进去的 Runnbale 对象的 run 方法吗?
- /**
- * 模拟开始
- */
- private void doSth() {
- //开启个线程,处理复杂的业务业务
- new Thread(new Runnable() {
- @Override
- public void run() {
- //模拟很复杂的业务,需要1000ms进行操作的业务
- ......
- handler.post(new Runnable() {
- @Override
- public void run() {
- //在这里可以更新ui
- mTv.setText("在这个点我更新了:" + System.currentTimeMillis());
- }
- });
- }
- }).start();
- }
我们回到 handler.dispatchMessage(Message) 中, 如果不是通过 post 那么 callback 就为空,我们看到了一个 mCallback 变量,我们看看这个 Callback 的定义:
- /**
- * Callback interface you can use when instantiating a Handler to avoid
- * having to implement your own subclass of Handler.
- *
- * @param msg A {@link android.os.Message Message} object
- * @return True if no further handling is desired
- */
- public interface Callback {
- public boolean handleMessage(Message msg);
- }
- /**
- * Constructor associates this handler with the {@link Looper} for the
- * current thread and takes a callback interface in which you can handle
- * messages.
- *
- * If this thread does not have a looper, this handler won't be able to receive messages
- * so an exception is thrown.
- *
- * @param callback The callback interface in which to handle messages, or null.
- */
- public Handler(Callback callback) {
- this(callback, false);
- }
The callback interface in which to handle messages, or null. 我们可以通过实现这个接口,并作为一个参数传进去 Handler 来达到处理这个消息的效果。
Handler.java:
- /**
- * Handle system messages here.
- */
- public void dispatchMessage(Message msg) {
- //这里先判断callback是否为空
- // callback就是我们使用handler.post(Runnable r)的入参runnable
- if (msg.callback != null) {
- handleCallback(msg);
- } else {
- //如果hanlder的入参callback不为空,优先处理
- if (mCallback != null) {
- //如果回调返回true.则拦截了handler.handleMessage的方法
- if (mCallback.handleMessage(msg)) {
- return;
- }
- }
- //这就是为什么我们使用hanlder的时候,需要重写handleMessage的方法
- handleMessage(msg);
- }
- }
最后一行代码中,我们看到了熟悉的 handleMessage,这不就是我们经常 handler.handlerMessage 的方法吗? Demo:
- Handler handler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- super.handleMessage(msg);
- //处理消息
- }
- };
但注意之前我们所看到的,如果我们 mCallback.handlerMessage(msg) 返回为 true 的话,这样就不交给 handler.handleMessage 处理了。
我们继续看回来我们的 Looper.loop() Looper.java:
- /**
- * 调用此函数用于启动消息队列循环起来,作用相当于工厂流水线中的传送带的开关,
- * 只有把开关打开,传送带才跑起来
- * Run the message queue in this thread. Be sure to call
- * {@link #quit()} to end the loop.
- */
- public static void loop() {
- .....
- .....
- //循环通过消息队列来获取消息
- for (; ; ) {
- ......
- //最后回收这个message
- msg.recycleUnchecked();
- }
- }
在无限循环每个消息的时候,除了调用 handler.dispatchMessage, 最后还会调用 msg.recycleUnchecked() 进行回收这个消息,至于 message 怎么回收我们就不讨论了,详情的大家可以去看看我上一篇文章。
现在我们回过头看看我之前说的那 2 个问题:为什么子线程创建 handler 会抛异常? 我们先看看 Handler 的关键的构造器:
- /**
- * Default constructor associates this handler with the {@link Looper} for the
- * current thread.
- *
- * If this thread does not have a looper, this handler won't be able to receive messages
- * so an exception is thrown.
- */
- public Handler() {
- this(null, false);
- }
- /**
- *......
- */
- public Handler(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());
- }
- }
- //获取当前线程的looper
- mLooper = Looper.myLooper();
- //如果当前线程没有绑定looper则抛异常
- if (mLooper == null) {
- throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");
- }
- mQueue = mLooper.mQueue;
- mCallback = callback;
- mAsynchronous = async;
- }
看构造器可得,如果在子线程创建 handler,必须在子线程中先创建一个 Looper 对象,不然 hanlder 在初始化的时候获取不了当前线程的 looper,会抛出异常 "Can't create handler inside thread that has not called Looper.prepare()"。
因此,如果我们在子线程中创建 Handler 的时候,我们可以这样:
- new Thread(new Runnable() {
- @Override
- public void run() {
- //在当前子线程创建一个Looper对象
- Looper.prepare();
- Handler handler=new Handler();
- //让消息队列动起来
- Looper.loop();
- }
- }).start();
这样就能成功在子线程创建 handler。
1. 为什么在主线程中创建 Handler 不需要我们调用 Looper.prepare(). 因为在程序的入口中系统会调用 Looper.prepareMainLooper() 来创建,并且让其主线程的 Looper 启动起来。如果我们在子线程创建 handler,需要手动创建 looper 并且启动。
2. 每一个线程只能存在一个 Looper, Looper 有一个全局变量 sThreadLocal 用来保存每一个线程的 looper, 通过 get、set 进行存取 looper。
3.Handler 可以通过通过 post 或者 sendMessage 进行发送消息,因为其最终会调用 sendMessageDelayed,我们可以通过 runnable 方式或者重写 handleMessage 进行消息的处理,当然如果通过 handler.sendMessage(msg) 的方式的话,我们可以实现 Callback 接口达到消息的处理。
4. 为什么不能在子线程更新 UI?其实更准确的来说应该是 UI 只能在创建 UI 的线程中进行更新,也就是主线程,如果子线程创建 UI,其可以在子线程进行更新。
注: 网上也有很多文章关于 Android 消息机制的文章,大神们都写得很透彻,但为什么我还要去写这样的文章,一方便想通过读源码来提高个人的学习能力,另一方面也想将自己学到的分享给大家,哪怕多年以后看回自己的博客,也是别有一番滋味。从 14 年开始出来做 android,看了不少文章,学到了很多东西,我很敬佩他们。特别感谢郭霖、李纪钢、徐宜生等等,因为他们的无私分享让很多人在工作上解决了不少难题,因此我也希望我能成为这样的人。
来源: https://juejin.im/post/5a31d6a751882554bd51100b