大多数情况下,handleMessage所在线程和 handler 初始化所在的线程相同,但 handler 初始化的时候可以传入一个 Looper 对象,此时handleMessage所在线程和参数looper所在线程相同。

1. 含参构造public Handler(Looper looper)

class MainActivity : AppCompatActivity() {var handler: Handler? = nullvar looper: Looper? = nulloverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)looper = Looper.getMainLooper()val thread = object : Thread() {override fun run() {super.run()Log.e("abc", "--- Runnable:threadName ---" + Thread.currentThread().name)handler = object : Handler(looper) {override fun handleMessage(msg: Message?) {super.handleMessage(msg)Log.e("abc","--- handleMessage:threadName ---" + Thread.currentThread().name)}}}}thread.start()myBtn.setOnClickListener {val msg = Message()handler!!.sendMessage(msg)}}
}// log 打印情况
--- Runnable:threadName ---Thread-2
--- handleMessage:threadName ---main

从 log 中可以看到 handler 初始化所在线程在 Thread-2,而handleMessage所在的线程是主线程main.

2. 无参构造

如果使用无参的 Handler 初始化构造,需要手动调用Looper.prepare()Looper.loop()

val thread = object : Thread() {override fun run() {super.run()Log.e("abc", "--- Runnable:threadName ---" + Thread.currentThread().name)Looper.prepare()handler = object : Handler() {override fun handleMessage(msg: Message?) {super.handleMessage(msg)Log.e("abc", "--- handleMessage:threadName ---" + Thread.currentThread().name)}}Looper.loop()}}// log 打印情况
--- Runnable:threadName ---Thread-2
--- handleMessage:threadName ---Thread-2

不手动调用Looper.prepare()会抛出异常:

java.lang.RuntimeException: Can't create handler inside thread Thread[Thread-2,5,main] that has not called Looper.prepare()

主线程中使用 Handler
大多数时候我们不会在子线程中初始化和使用 handler,而是在主线程中使用,此时不需要prepare()loop(),因为主线程中自带一个 Looper(通过Looper.getMainLooper()可以获取)

3. 一个线程可以有多少个 Looper?Handler 和 Looper 之间如何关联?

3.1 一个线程可以有多少个 Looper

查看Looper.prepare()源码:

private static void prepare(boolean quitAllowed) {if (sThreadLocal.get() != null) {throw new RuntimeException("Only one Looper may be created per thread");}sThreadLocal.set(new Looper(quitAllowed));}

继续查看sThreadLocal.set(new Looper(quitAllowed))

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

Threadlocal 是一个线程内部的存储类,可以在指定线程内存储数据,数据存储以后,只有指定线程可以得到存储数据。在这里 ThreadLocal 的作用是保证了每个线程都有各自的 Looper,就是说一个线程只能有一个 Looper,关于 Threadlocal,可以看看这篇文章 Threadlocal

接下来看看创建 Looper 实例的方法new Looper(quitAllowed)

final MessageQueue mQueue;
final Thread mThread;
private Looper(boolean quitAllowed) {mQueue = new MessageQueue(quitAllowed);mThread = Thread.currentThread();

在构造方法里,初始化 了MessageQueue 和代表当前线程的属性 mThread.

调用Looper.prepare()其实就是利用 ThreadLocal 为当前的线程创建了一个独立的 Looper,这其中包含了一个消息队列

3.2 Handler 和 Looper 之间如何关联

一个线程只能有一个 Looper,但一个线程中可以创建多个 Handler,那么一个 Looper 怎么和多个 Handler 对应呢?查看源码可知,post(Runnable r)postDelayed(Runnable r, long delayMillis)postAtTime(Runnable r, long uptimeMillis)sendMessage最终调用的都是enqueueMessage方法:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {msg.target = this;if (mAsynchronous) {msg.setAsynchronous(true);}return queue.enqueueMessage(msg, uptimeMillis);}

msg.target = this这里就是将当前的 Handler 赋值给 Message 对象的 target 属性,这样在处理消息的时候通过msg.target就可以区分开不同的 Handler 了。

Handler 中的 handleMessage 所在线程是由什么决定的?相关推荐

  1. Android Handler中的handleMessage方法和post方法之源码剖析

    我们都知道,在子线程中进行UI操作(更新UI控件)包括以下四种方法: 1.Handler的handlerMessage()方法. 2.Handler的post()方法. 3.View的post()方法 ...

  2. 安编程中使用handleMessage实现线程之间数据交互

    更多最新安卓编程资料请关注微信公众号:安卓编程入门进阶 百度云原清晰度地址:http://pan.baidu.com/s/1mhS0H8O 本节课介绍HandleMessage在线程之间传递数据.由于 ...

  3. handler.handleMessage(msg) 和 handler.sendEmptyMessage()运行在主线程吗?

    遇到问题: 1.  平时 Handler 使用方式, 1. 定义 private Handler handler = new Handler() {@Override public void hand ...

  4. 安卓中的几种线程间通信方式

    一:Handler实现线程间的通信 andriod提供了 Handler 和 Looper 来满足线程间的通信.例如一个子线程从网络上下载了一副图片,当它下载完成后会发送消息给主线程,这个消息是通过绑 ...

  5. Handler 基本用法--线程间传值

    Handler 作用:主要接受子线程发送的数据, 并用此数据配合主线程更新UI. 解释: 当应用程序启动时,Android首先会开启一个主线程 (也就是UI线程) , 主线程为管理界面中的UI控件,进 ...

  6. 掌握Android中的进程和线程

    进程和线程是现代网络操作系统的核心概念.Android作为一种优秀的.承袭Linux的移动操作系统,其进程和线程的概念是开发者和安全工作人员需要深入了解的问题.本文将详细介绍Android中的进程.线 ...

  7. Java 中的几种线程池,你之前用对了吗,互联网 面试官 如何面试

    写在最前面,我总结出了很多互联网公司的面试题及答案,并整理成了文档,以及各种学习的进阶学习资料,免费分享给大家.扫码加微信好友进[程序员面试学习交流群],免费领取.也欢迎各位一起在群里探讨技术. 好久 ...

  8. Android中的进程和线程

    写在前面的话 一个Android应用就是一个Linux进程,每个应用在各自的进程中运行,互不干扰,比较安全. 一个应用对应一个主线程,就是通常所说的UI线程,android遵守的就是单线程模型,所以说 ...

  9. Android Handler中post方法与send方法的区别及使用

    目录 概述 Handler使用sendMessage方法 Handler使用post方法 post方法与send方法的区别 全部代码 效果图 后记 概述 Handler机制是Android中线程通信的 ...

最新文章

  1. 微软宣布MySQL和PostgreSQL的Azure数据库服务正式可用
  2. PostgreSQL9.5和JSONB的强大功能
  3. vc设置按钮文字颜色
  4. The Best Vacation CodeForces - 1358D(贪心+尺取)
  5. [Spring5]IOC容器_Bean管理XML方式_注入其他类型属性
  6. mysql数据库怎么读文件_mysql数据库读写文件
  7. HTML5学习笔记之音视频标签
  8. 笔试错题--(字符串常量池和JVM运行时数据区)
  9. 计算机毕业设计最新选题汇总(持续更新)
  10. colspan会影响内部单元格宽度失效_封装胶残留致MEMS振动传感器失效分析
  11. 正确方式安装Acrobat DC(附安装包)
  12. 抖音返利分销模式及代理系统开发
  13. python的OOP机制
  14. 设计师必备15个超赞的配色网站,从此配色无忧!
  15. 【面经】2018金山WPS前端笔试题 面试题
  16. 蜻蜓FM回应恶意代码事件 音频行业仍将现721格局
  17. 美国签证申请中的行政审查制度(图)
  18. 机器学习中分类与聚类的本质区别
  19. 运维(1) Jenkinsfile+Dockerfile+Nginx实现前端Vue自动化部署
  20. SQL语句实现关系代数中的“除法”

热门文章

  1. 字节跳动客户端开发面经(一面)
  2. HttpUnit模拟按钮点击以及爬虫实现
  3. Javascript和BHO的相互调用简介
  4. 主板芯片组与内存映射
  5. etCapture() 和 ReleaseCapture()的用法
  6. request 和 session 的区别联系
  7. 神经网络算法有哪些模型,常用的神经网络模型
  8. jsonp 的原理及应用
  9. Leetcode 1227:飞机座位分配概率
  10. 机器人抓取平台搭建记录(二):KINOVA Gen2的控制--手柄控制