在 android Handler 介绍(一) 中的第一个例子:首先通过一个 无参 Handler 构造函数实例化一个 Handler 类型的全局变量,并重写其 handleMessage 方法,在某一方法内调用 Handler 的 sendEmptyMessage 或者 sendMessage 发送消息,在某一时刻 handleMessage 回调方法会被调用 。
Handler 内部定义的两个 final 对象
final MessageQueue mQueue ;
final Looper mLooper ;
MessageQueue 即为其内部的消息队列, mQueue 是通过 Looper 的静态变量 mQueue 直接赋值的。而 mLooper 是通过 Looper 的静态方法 myLooper() 赋值。进入该方法,
Looper.myLooper() 实质上调用了 sThreadLocal.get() 来获取与当前线程相关联的 Looper ,源码里面有这样一句话
// sThreadLocal.get() will return null unless you've called prepare().
,在调用该方法之前必须先调用 prepare() 方法,否则该方法返回 null 。但事实上我们并没有调用这个方法,却能成功,原因是为什么呢?
Looper 内部提供了一个静态方法 prepareMainLooper ,这个方法是在应用程序创建的时候直接由 android 系统调用的。
/** 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.
*/
该方法调用了 prepare () 方法和 setMainLooper (myLooper ()) 并将 myLooper () 返回的值设置为主线程的 looper 。
接下来看 sendEmptyMessage 和其他几个发送消息的方法,它们最终都会调用到 sendMessageAtTime 这样一个方法,而这个方法会调用 MessageQueue 提供的 enqueueMessage 方法,该方法将发送的消息放入消息队列里面,最后调用
nativeWake(mPtr) 方法,这是一个 C++ 实现的方法,具体的实现内容没有深究,但应该间接调用到 Looper 的 loop() 方法。
接下来看一下 loop() 方法,这个方法从消息队列中取出消息,然后调用里面存放的 target 的 dispatchMessage 方法:
msg.target.dispatchMessage(msg);
msg.target 存放的是在构造消息的类的 handler 对象,事实上这里回调了 handler 的 dispatchMessage 方法,该方法会调用 handleMessage 方法。
android Handler 介绍(一) 中的第二个例子:定义一个 runnable ,并重写其 run 方法,通过 Handler 的 post 方法或 postDelayed 方法来实现消息处理。
与 sendEmptyMessage 方法类似, post 方法或 postDelayed 方法最终也会调用到 sendMessageAtTime 方法。稍微有点不同的是,发送的消息的结构有点不一样。 post 方法通过调用 getPostMessage(Runnable r) 获取消息,该方法把 runnable 对象封装成 Message 。接下来的处理过程与上面类似。在 dispatchMessage 方法里处理的时候,会先判断收到的消息中的 runnable 是不是空,如果不为空,则先取出,调用, runnable 内部重写的 run 方法:
message.callback.run();
如果在 run 方法中再调用方法,上述流程会重新再走一遍,这就是第二个例子为什么会反复运行的原因了。
接下来分析线程中的 handler 。默认情况下,线程与 looper 并不直接关联(主线程除外),线程通过调用 Looper.prepare() 方法来与 looper 建立联系。在 Looper 里面定义了一个全局的静态类 sThreadLocal
private static final ThreadLocal sThreadLocal = new ThreadLocal();
ThreadLocal 实现了线程的本地存储,也就是说,对于不同的线程,同一个变量有不同的值。所有的线程共享一个共同的 ThreadLocal 对象,但访问不同的线程时可以得到不同的值,修改这个值并不影响到其他线程。
Looper.prepare() 方法通过调用 sThreadLocal.set(new Looper()) ,将一个 looper 与所在的线程组成对放入 ThreadLocal 中, ThreadLocal 维护了一张 Object 类型的 table 表用来存放这两个对象, Looper.myLooper() 调用其 get 方法将其取出。在另起的线程中,如果不调用 Looper.prepare() 方法,则从里面取出的为 null 。
通过之前的分析,重写后的 handleMessage 是被 Handler 里面的 dispatchMessage 方法调用的,而 dispatchMessage 方法被 Looper 里面的 loop() 所调用,换句话说, Handler 里面的 mLooper 所在的线程决定了 handleMessage 方法所在的线程,如果我们在构造 handler 的时候传入一个非主线程相关联的 looper 则 handleMessage 在该线程中运行。