续学习 Android Handler 消息机制需要注意这些问题!(上)www.jianshu.com/writer#/not… 2.2 Handler 的创建和作用
上面说到 loop() 方法在不断从消息队列 MessageQueue 中取出消息(queue.next() 方法),如果没有消息则阻塞,反之交给 Message 绑定的 Handler 处理。回顾一下没解决的两个问题:
-问题3:MessageQueue 里的消息从哪里来?Handler 是如何往 MessageQueue 中插入消息的?
既然要解决 Handler 插入消息的问题,就要看 Handler 发送消息的过程。
2.2.1 Handler 发送消息
Handler --> sendMessage(Message msg);
final MessageQueue mQueue; public final boolean sendMessage(Message msg){ return sendMessageDelayed(msg, 0); } // 发送延时消息 public final boolean sendMessageDelayed(Message msg, long delayMillis){ if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); } // 指定时间发送消息 public boolean sendMessageAtTime(Message msg, long uptimeMillis) { MessageQueue queue = mQueue; if (queue == null) { RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue"); Log.w("Looper", e.getMessage(), e); return false; } return enqueueMessage(queue, msg, uptimeMillis); } // 处理消息,赋值 Message 对象的 target,消息队列插入消息 private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
可以看到调用 sendMessage(Message msg) 方法最终会调用到 enqueueMessage() 方法,这个方法主要有两个作用:赋值 Message 对象的 target、消息队列插入消息。
- 赋值 msg 的 target:msg.target = this 把发送消息的 Handler 赋值给 msg 对象的 target。那么问题 4 就解决了:Handler 执行发送消息的过程中将自己绑定给了 Message 的 target,这样两者之间就产生了联系;
- 消息队列插入消息:queue.enqueueMessage(msg, uptimeMillis) queue 是 MessageQueue 的一个实例,queue.enqueueMessage(msg, uptimeMillis)是执行 MessageQueue 的enqueueMessage方法来插入消息。这样问题 3 就找到答案:Handler 在发送消息的时候执行 MessageQueue 的enqueueMessage方法来插入消息;关于 MessageQueue 是怎么执行插入消息的过程,参考下方文章 4.3 节
Android消息机制1-Handler(Java层)
- 上面 Handler 发送消息使用了 MessageQueue 的实例 queue,可以看到这个 queue 是上一个方法 sendMessageAtTime 中由 Handler 的成员变量 mQueue 赋值的,那么 mQueue 是哪来的?问题 5:Handler 如何绑定 MessageQueue?先剧透一下 Handler 绑定的是 Looper 的 MessageQueue 对象,Looper 的 MessageQueue 对象是在 Looper 创建时就 new 的。 要了解 Handler 的 MessageQueue 对象是怎么赋值的就要看 Handler 的构造函数了,Handler 创建的时候作了一些列操作比如获取当前线程的 Looper,绑定 MessageQueue 对象等。
2.2.2 Handler 的创建
下面是 Handler 无参构造器和主要的构造器,另外几个重载的构造器有些是通过传递不同参数调用包含两个参数的构造器。两个参数构造函数第一个参数为 callback 回调,第二个函数用来标记消息是否异步。
// 无参构造器 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()); } } // step1:获取当前线程 Looper mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } // step2:获取 Looper 对象绑定的 MessageQueue 对象并赋值给 Handler 的 mQueue mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; }
- step1:调用myLooper() 方法,该方法是使用 sThreadLocal 对象获取当前线程的 Looper 对象,回顾一下:
public static @Nullable Looper myLooper() { return sThreadLocal.get(); }
如果获取的 Looper 对象为 null,说明没有执行 Looper.prepare() 为当前线程保存 Looper 变量,就会抛出 RuntimeException。这里又说明了Handler 必须在有 Looper 的线程中使用,报错不说,没有 Looper 就无法绑定 MessageQueue 对象也就无法进行更多有关消息的操作。
-
step2:mQueue = mLooper.mQueue 说明了 Handler 的 MessageQueue 对象是由当前线程 Looper 的 MessageQueue 对象赋值的。这里问题 5 解决:Handler 在创建时绑定了当前线程 Looper 的 MessageQueue 对象。
-
由于 Handler 和 Looper 可以看作使用的是同一个 MessageQueue 对象,所以 Handler 和 Looper 可以共享消息队列 MessageQueue。Handler 发送消息(用 mQueue 往消息对列插入消息),Looper 可以方便的循环使用 mQueue 查询消息,如果查询到消息,就可以用 Message 对象绑定的 Handler 对象 target 去处理消息,反之则阻塞。
既然说到了 Handler 的构造器,就想到一个问题:**问题 6:关于 handler,在任何地方 new handler 都是什么线程下?**这个问题要分是否传递 Looper 对象来看。
-
不传递 Looper 创建 Handler:Handler handler = new Handler();上文就是 Handler 无参创建的源码,可以看到是通过 Looper.myLooper() 来获取 Looper 对象,也就是说对于不传递 Looper 对象的情况下,在哪个线程创建 Handler 默认获取的就是该线程的 Looper 对象,那么 Handler 的一系列操作都是在该线程进行的。
-
传递 Looper 对象创建 Handler:Handler handler = new Handler(looper);那么看看传入 Looper 的构造函数:
public Handler(Looper looper) { this(looper, null, false); } public Handler(Looper looper, Callback callback) { this(looper, callback, false); } // 第一个参数是 looper 对象,第二个 callback 对象,第三个消息处理方式(是否异步) public Handler(Looper looper, Callback callback, boolean async) { mLooper = looper; mQueue = looper.mQueue; mCallback = callback; mAsynchronous = async; }
可以看出来传递 Looper 对象 Handler 就直接使用了。所以对于传递 Looper 对象创建 Handler 的情况下,传递的 Looper 是哪个线程的,Handler 绑定的就是该线程。
到这里 Looper 和 Handler 就有一个大概的流程了,接下来看一个简单的子线程 Handler 使用例子:
new Thread() { @Override public void run() { // step1 Looper.prepare(); // step2 Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { if(msg.what == 1){ runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(MainActivity.this,"HandlerTest",Toast.LENGTH_SHORT).show(); } }); // step5 Looper.myLooper().quit(); } } }; // step3 handler.sendEmptyMessage(1); // step4 Looper.loop(); } }.start();
- step1: 调用 Looper.prepare(); 为当前线程创建 Looper 对象,同时也就创建了 MessageQueue,之后将该线程的 Looper 对象保存在 ThreadLocal 中。注意这里的一切操作都在子线程中,如果不调用 Looper.prepare() 就使用 Handler 会报错。
- step2: 创建 Handler 对象,覆写 handleMessage 处理消息,等待该 Handler 发送的消息处理时会调用该方法。
- step3: 使用 handler 发送消息,这里只是示例,毕竟自己给自己发送消息没啥必要。发送的过程中会将自己赋值给 msg.target,然后再将消息插入到 Looper 绑定的 MessageQueue 对象中。
- step4: 调用 Looper.loop(); 首先获取当前线程的 Looper 对象,根据 Looper 对象就可以拿到 Looper 保存的 MessageQueue 对象 mQueue。有了 MessageQueue 对象就可以 for 循环获取它保存的消息 Message 对象,如果消息不存在就返回 null 阻塞,反之则使用 Message 中保存的 Handler:msg.target 来处理消息,最终调用 handleMessage 也就是之前覆写的方法来处理消息。
- step5: 逻辑处理完毕以后,应在最后使用 quit 方法来终止消息循环,否则这个子线程就会一直处于等待的状态,而如果退出Looper以后,这个线程就会立刻终止,因此建议不需要的时候终止Looper。 ##三、总结和其它
3.1 Handler、Looper、MessageQueue、Message
- Handler 用来发送消息,创建时先获取默认或传递来的 Looper 对象,并持有 Looper 对象包含的 MessageQueue,发送消息时使用该 MessageQueue 对象来插入消息并把自己封装到具体的 Message 中;
- Looper 用来为某个线程作消息循环。Looper 持有一个 MessageQueue 对象 mQueue,这样就可以通过循环来获取 - MessageQueue 所维护的 Message。如果获取的 MessageQueue 没有消息时,便阻塞在 loop 的queue.next() 中的 nativePollOnce() 方法里,反之则唤醒主线程继续工作,之后便使用 Message 封装的 handler 对象进行处理。
- MessageQueue 是一个消息队列,它不直接添加消息,而是通过与 Looper 关联的 Handler 对象来添加消息。
- Message 包含了要传递的数据和信息。
最后
对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。整理的这些架构技术希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。
同时我经过多年的收藏目前也算收集到了一套完整的学习资料以及高清详细的Android架构进阶学习导图及笔记免费分享给大家,希望对想成为架构师的朋友有一定的参考和帮助。
下面是部分资料截图,诚意满满:特别适合有开发经验的Android程序员们学习。
资料免费领取方式:点击我的GitHub~
不论遇到什么困难,都不应该成为我们放弃的理由!
如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言,一定会认真查询,修正不足,谢谢。