1. 什么是 Handler
我们平时在开发中, 经常用到 Handler, 用来发送消息, 处理消息. 或者做一些延迟发送消息, 跨线程发消息, 或者更新 UI, 或者去实现一些定时轮询的操作. 安卓发展至今, 已经有很多框架, 可以代替这样原生 Handler 的通信方式. 比如 Eventbus, Rxjava,AyncTask... 等等. 但是实际上底层依然是对 Handler 的封装. 那么 Handler 究竟是什么?
简而言之:
Handler 就是 Android 系统提供给我们, 用来更新 UI 的一套机制, 也是一套消息处理机制. 通过 Handler, 可以用来发送消息, 处理消息. 如果不遵循这样的机制, 就没有办法更新 UI, 会抛出异常.
2. Handler 消息机制的五个重要角色
1.Handler: 消息的发送和处理者
2.Message: 消息
3.MessageQueue: 消息存放的队列
4.Looper : 从消息队列里面一条一条取消息的消息侦听器, 或者消息泵
5. 线程: 当前是在哪个线程
3. 通俗的理解这个消息机制
也许很多人在网上都看过很多资料去解释这样一个消息机制, 但是如果向别人阐述这样一个原理的时候, 或者面试的时候, 我相信很多人还是模棱两可, 弄不太清楚. 那么, 通过一个简单的生活案例, 来帮助理解一下:
eg: 我们都知道读书的时候, 我们经常需要向老师请假之类.
比如: 报告老师, 我要上洗手间. 然后老师说: 允许, 快去吧.
过了一会.
又给老师报告: 报告老师, 后面有同学踢我凳子. 老师说: 你先坐前面来, 不要理他.
当然, 这只是比喻. 我们通过这个比喻, 来比较生动形象的去理解 handler 就容易多了.
1.handler: 学生.
2.message: 报告老师的内容
3.Looper : 老师自己
4.messagequeue: 老师的耳朵和听力记忆
解释:
学生 (handler) 向老师 (Looper) 举手
说 "我要上洗手间" 这个报告(message),
然后老师的耳朵 (messagequeue) 听到了这个报告,
先是反馈了学生的请求(dispatchHandle), 告诉这个同学, 同意他的请求,
于是学生自己就上洗手间去了(handleMessage)
ps:
老师:
在这个过程中, 就是一个消息的接受者, 源源不断的接受学生的各种报告或者消息, 存在老师的记忆里,
老师又没有分身术, 只有一张嘴, 所以同一时间, 只能根据记忆中的消息, 一条一条的反馈给学生, 处理他们的请求.
学生:
在这个过程中, 我们可以发现, 学生既是消息的发出者, 又是消息的处理者.
收到老师的同意后, 于是就高高兴兴的去上洗手间去了.
整个过程如图:
由图可以看出:
1.handler 负责发送消息, 接收处理消息
2.Looper 负责接收 handler 发过来的消息
3.MessageQueue 就做为 Looper 消息泵内部的消息容器 3.Looper 在内部, 将发送过来的消息, 交给自身的消息队列, 并按时间顺序加入其中 4.Looper 在内部, 通过不断循环的方式, 将消息从队列中一个一个取出, 回传给 handler
此时你有可能会问: 那么 Handler 它怎么知道应该往哪发消息, 并且发给哪个 Looper, 加入这个 Looper 的消息队列呢? 答案在构造方法中.
4.Handler 的创建和 Looper 的关联关系
Handler 的创建, 调用 Handler 的构造方法即可. new Handler(). 那么我们看下 Handler 的构造方法都做了些什么呢? Handler 的构造方法有好几个, 我们先从空构造看起:
- // 从这开始看起, 空构造
- public Handler() {
- this(null, false);
- }
- public Handler(Callback callback) {
- this(callback, false);
- }
- public Handler(Looper looper) {
- this(looper, null, false);
- }
- public Handler(Looper looper, Callback callback) {
- this(looper, callback, false);
- }
- public Handler(boolean async) {
- this(null, async);
- }
- // 然后看到这个构造方法
- 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 对象, 并且这个对象是通过 myLooper 得到, 也就是当前线程的 looper.
- // 当前线程: 默认情况就是主线程, 为什么会是主线程, 后面会讲
- mLooper = Looper.myLooper();
- 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;
- }
- public Handler(Looper looper, Callback callback, boolean async) {
- mLooper = looper;
- mQueue = looper.mQueue;
- mCallback = callback;
- mAsynchronous = async;
- }
上面代码可以看出, 在 handler 的默认构造器中, 有一句: mLooper=Looper.myLooper(). 这说明 Handler 默认的时候, 有一个 Looper 对象. 但是这个 Looper 对象是怎么创建得来的呢? 点击去发现: 就是根据当前线程返回一个 looper 对象. 当前线程是什么呢? 默认情况下, 就是主线程, 也就是 MainLooper.
- /**
- * Return the Looper object associated with the current thread. Returns
- * null if the calling thread is not associated with a Looper.
- */
- // 解释一下: 根据当前线程, 返回 looper 对象. 如果线程没有与之关联的 Looper, 那么返回空
- public static @Nullable Looper myLooper() {
- return sThreadLocal.get();
- }
好了, 看到这里, 我们就看到了 Handler 和一个默认 looper 对象如何关联的. 至于 looper 对象的如何创建, 后面再详细讲解.
我们可以得出结论:
默认情况下: new Handler()中的 looper 对象是主线程的 looper 对象 , 那么消息就是发给主线程的 handler 进行处理.
需要和特定线程的 Handler 通信, 我们就需要调用 new Handler(Looper looper) , 传入特定线程的 Looper 对象即可.
Looper 对象是属于什么线程, 那么 handler, 就是往哪个线程进行发送消息和处理消息.
5. 发送消息的过程
搞清楚 Handler 和 Looper 如何进行关联的关系以后, 我们从消息的发送开始理解.
Handler 如何发送消息, 消息从哪里产生? 要搞清楚消息的产生, 我们首先要知道 handler 发送一条空消息是 sendEmptyMessage(). 那么我们就跟踪这个方法: 方法的参数 what , 就是一个标记位, 先不管它.
- /**
- public final boolean sendEmptyMessage(int what)
- {
- return sendEmptyMessageDelayed(what, 0);
- }
- 再继续往下跟踪
- // 这里可以看到, sendEmptyMessage()默认会调用 sendEmptyMessageDelayed()方法
- public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
- // 内部, 通过 Message.obtain(), 产生了一条消息 Message
- Message msg = Message.obtain();
- msg.what = what;
- // 然后再调用 sendMessageDelayed()方法
- return sendMessageDelayed(msg, delayMillis);
- }
- public final boolean sendMessageDelayed(Message msg, long delayMillis)
- {
- if (delayMillis <0) {
- delayMillis = 0;
- }
- return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
- }
- 这里可以看到发送空消息时, Message 是在方法内部, 通过 Message.obtain 产生的. 并且会层层调用, 最终是这么一个方法路径:
- sendEmptyMessage-->sendMessageDelay-->sendMessageAtTime , 最终都是调用 sendMessageAtTime()来发送消息的.
- 6. 消息发出后的过程
- 通过上面 sendMessageAtTime()发送消息, 消息产生后, 按照前面的图的理解, 它是会发送出一个 Messgae, 并且发给 Looper, 由 Looper 加入到消息队列的. 那么代码上是怎样的呢?
- public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
- //1. 首先获取到 queue 对象,
- MessageQueue queue = mQueue;
- if (queue == null) {
- RuntimeException e = new RuntimeException(
- this + "sendMessageAtTime() called with no mQueue");
- Log.w("Looper", e.getMessage(), e);
- return false;
- }
- //2\. 将消息, 插入到这个消息 queue 队列中去, 并且附带了一个时间值
- return enqueueMessage(queue, msg, uptimeMillis);
- }
- 我们可以看出:
- 在最终发送消息的地方, 拿到了 Queue 队列对象, 然后就把消息加入到了这个 Queue 队列中去了. 这个 Queue 对象的获取, 我们可以观察 handler 的构造看到: 每一次 Handler 创建的时候, 会拿到 looper 对象, 再通过 looper 对象来获取到内部的这个 Queue 队列对象. 回顾 handler 的创建可发现, 如下:
- public Handler(Callback callback, boolean async) {
- ....
- ....
- //1 先拿到线程的 looper
- mLooper = Looper.myLooper();
- if (mLooper == null) {
- throw new RuntimeException(
- "Can't create handler inside thread that has not called Looper.prepare()");
- }
- //2 通过 looper 获取到消息 queue 队列
- mQueue = mLooper.mQueue;
- mCallback = callback;
- mAsynchronous = async;
- }
- 再看下消息加入 queue 队列的过程
- private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
- // 这里可以发现 消息 msg 的 target 属性: 是 this, 也就是 handler 自身当前对象
- msg.target = this;
- if (mAsynchronous) {
- msg.setAsynchronous(true);
- }
- return queue.enqueueMessage(msg, uptimeMillis);
- }
- 在加入队列的时候, 可以看到消息 msg 的地址, 也就是处理者 target(由谁来最终处理消息), 赋值为 this.
- 这也就说明了, handler 默认情况下, 发送消息的是它自己, 处理消息的也是它自己.
- 总结一下上面消息的发送过程:
- 发送出去 sendEmptyMessage(), 消息的产生在这个里面
- 调用 sendEmptyMessageDelay()
- 内部调用 sendMessageDelay()
- 内部调用 sendMessageAtTime()
- 内部通过当前线程, 首先获取到 mQueue 对象, 也就是 消息队列对象 MessageQueue
- 当 queue 不为空的情况下, 设置 target, 发送给谁, 默认是 this --handler 自己
- 然后把消息 message, 放到消息队列中. queue.enqueueMessage(消息, 时间);
- 7. 侦听消息和取消息的过程
- 我们知道, 消息发送到 Looper 中后, 就进入到了队列中, 然后等待 Looper 的循环取出进行分发. 那么 Looper.loop() 这个循环的过程是在什么时候触发的呢? 由于 Android 默认的线程是主线程, 所以在应用进程启动之后, 就会进入 ActivityThread 这个主线程中, 而在这个 ActivityThread 中的 main()方法, 就会触发 Looper.loop()开始侦听主线程的消息.
- //main 方法
- public static void main(String[] args) {
- Process.setArgV0("<pre-initialized>");
- 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);
- //1. 触发 loop
- Looper.loop();
- throw new RuntimeException("Main thread loop unexpectedly exited");
- 可以看到, 在主线程的 ActivityThread main()方法内部, 触发 Looper.loop(). 学过 java 的人都知道, main 方法是一个类的主入口.
- 那么 loop 是如何进行取消息的呢? 点进去继续往下看
- public static void loop() {
- // 1. 拿到当前线程 looper 对象
- final Looper me = myLooper();
- if (me == null) {
- throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
- }
- 2. 得到 looper 对象内部的消息队列 queue
- final MessageQueue queue = me.mQueue;
- ...
- ...
- //3. 死循环, 配合队列进行取消息
- for (;;) {
- //4. 取出一条消息
- 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 slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
- final long traceTag = me.mTraceTag;
- if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
- Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
- }
- final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
- final long end;
- try {
- //4. 分发消息
- msg.target.dispatchMessage(msg);
- end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
- } finally {
- if (traceTag != 0) {
- Trace.traceEnd(traceTag);
- }
- }
- }
- }
- 可以看到:
- Looper.loop()方法中
- 2. 通过 myLooper, 拿到一个 Looper 对象
- 3. 取出 looper 中的消息队列 mQueue
- 4. 然后通过一个死循环, 里面通过 mQueue.next 来取消息
- 如果消息为空了, 就 return 掉,
- 5. 如果消息不为空, 就会调用
- msg.target.dispatchMessage(消息). 将消息发出去
- msg.target: 就是 handler 自己
- handler.dispatchMessage(消息)方法, 将消息回调到 dispatch 中去
- 消息如何分发给 handler 处理的呢
- 通过上面 Looper 取消息的过程. 我们看到了. 消息在最终取出后, 会通过 dispatchMsg 进行回传, 交给处理者. 这个处理者, 被赋值在 Msg.target 中. Msg 的 target 本质上就是 handler.
- 在前面发送消息, 将消息加入队列的时候, 我们看到过, msg.target=this, 是在那个时候将消息的处理者进行了赋值.
- 所以这里, 当取出消息后, 就又通过 handler, 通过它的 dispatchMessage(msg)进行回传的.
- }
- final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
- final long end;
- try {
- // 消息分发, 回传给处理者 . 也就是 handler 自己.
- msg.target.dispatchMessage(msg);
- end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
- } finally {
- if (traceTag != 0) {
- Trace.traceEnd(traceTag);
- }
- }
- 继续往下跟踪可以发现:
- /**
- * Handle system messages here.
- */
- public void dispatchMessage(Message msg) {
- // 如果消息的 callback 处理者不为空, 就通过这个 callback 进行处理. 这个 callback 是什么呢? 后续再讲解
- if (msg.callback != null) {
- handleCallback(msg);
- } else {
- // 默认情况下, 走这个分支
- if (mCallback != null) {
- // 如果全局的 callback 不为空, 就会执行这里, 不再执行 handlemessgae
- if (mCallback.handleMessage(msg)) {
- return;
- }
- }
- // 回传给消息处理者, 处理消息,
- handleMessage(msg);
- }
- }
分发消息后, 默认会进入第二个分支. 通常情况下 callback 是没有进行设置, 所以直接就会回调 handleMessage(msg). 走完整个流程. 进入处理消息流程
可是这个 msg.callback 是什么呢? 还有全局的 mCallback 又是什么呢? 分发消息的时候, 如果这些不为空. 又会触发什么呢? 继续往后看
handler 中的 Callback 又是什么
由于我们知道通知 UI 刷新的机制有 4 种方式:
- view.post(runnable)
- handler.post(runnable)
- runOnUiThread(runnable)
- handler.sendMsg(runnable)
我们跟踪一下 view.post(runnable). 进入 view 源码:
- // 传入一个 runnable
- public boolean post(Runnable action) {
- final AttachInfo attachInfo = mAttachInfo;
- if (attachInfo != null) {
- // 然后将 runnable, 交给 handler 的 post 执行
- return attachInfo.mHandler.post(action);
- }
- // Postpone the runnable until we know on which thread it needs to run.
- // Assume that the runnable will be successfully placed after attach.
- getRunQueue().post(action);
- return true;
- }
然后进入到 handler 的内部中 post
- public final boolean post(Runnable r)
- {
- return sendMessageDelayed(getPostMessage(r), 0);
- }
可以看到 runnable, 被包装进入到了发送消息的 getPostMessage(r)方法中. 跟进去:
- private static Message getPostMessage(Runnable r) {
- Message m = Message.obtain();
- // 看到没!!! callback, 原来就是指 我们使用 view.post(runnbale)的这个对象
- // 这个 runnable, 会赋值给 msg.callback.
- // 所以当我们使用别的方式进行更新 UI 或者消息通信的时候, 在 handleMessage 前, 就会进入 callback 不为空的判断
- m.callback = r;
- return m;
- }
也就是这里 msg.callback: 再贴一遍代码
- /**
- * Handle system messages here.
- */
- public void dispatchMessage(Message msg) {
- // 如果消息的 callback 处理者不为空, 就通过这个 callback 进行处理. 这里就是使用别的方式进行更新 UI 或者消息的时候, 进入这里处理, 不再进入 handleMessage
- if (msg.callback != null) {
- handleCallback(msg);
- } else {
- // 默认情况下, 走这个分支
- if (mCallback != null) {
- // 如果全局的 callback 不为空, 就会执行这里, 不再执行 handlemessgae
- if (mCallback.handleMessage(msg)) {
- return;
- }
- }
- // 回传给消息处理者, 处理消息,
- handleMessage(msg);
- }
- }
而 mCallback 则是我们初始化构造 handler 的时候, 传入的 callback, 进行拦截消息处理使用.
总结
我们可以发现, 最终都是进入到了 handler 的 post 方法中, 通过 handler 机制来实现.
未完待续....
继续讲解更深入的细节...
8Looper 的创建过程
这里发现有一个 sThreadLocal 对象, 我们的 looper 对象就是从这个对象中获取的. 这个 ThreadLocal 对象, 其实就是一个与线程相关的对象, 保存了线程的相关变量, 状态等等. 再继续往下看: 发现最终就是从这个线程相关的对象中, 内部有一个 map 对象, 从里面获取得来. 这个地方只是一个单纯的 get
- public T get() {
- Thread t = Thread.currentThread();
- ThreadLocalMap map = getMap(t);
- if (map != null) {
- ThreadLocalMap.Entry e = map.getEntry(this);
- if (e != null) {
- @SuppressWarnings("unchecked")
- T result = (T)e.value;
- return result;
- }
- }
- return setInitialValue();
- }
这个地方依然不是我们想要的答案, 我们想看的是 Looper 对象是在哪里创建的.
今年金九银十我花一个月的时间收录整理了一套知识体系, 如果有想法深入的系统化的去学习的, 可以点击传送门, 我会把我收录整理的资料都送给大家, 帮助大家更快的进阶.
来源: http://www.jianshu.com/p/9eb087dfa9aa