Handler 源码分析

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 在该线程中运行。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值