Handler 中的 handleMessage 所在线程是由什么决定的?
大多数情况下,handleMessage
所在线程和 handler 初始化所在的线程相同,但 handler 初始化的时候可以传入一个 Looper 对象,此时handleMessage
所在线程和参数looper
所在线程相同。
1. 含参构造public Handler(Looper looper)
class MainActivity : AppCompatActivity() { var handler: Handler? = null var looper: Looper? = null override 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 了。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
云原生基础架构的最佳状态,就是没有架构?
云栖号资讯:【点击查看更多行业资讯】在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来! 云原生基础架构是通向云原生时代的基石,对于很多架构师来说,上云之后,架构为什么成为了云原生架构而不是传统的架构,两者有何区别?云原生基础架构是如何演进的?本文进行了全面梳理。 什么不是云原生基础架构? 云原生被谈的很多了,导致概念很乱。有人把云原生基础架构和公有云、容器、容器编排系统等划等号,之所以出现这种情况,原因是云原生架构并没有一个统一的概念。 为了更好的理解云原生系统,这里先做一些排除。 首先,云原生并不等于公有云。云原生基础架构不仅仅是在公有云上运行基础架构,这是因为仅仅从云服务商那里租用服务器时长,并不会使你的基础架构云原生化,管理IaaS和运行物理数据中心本质上没区别。 其次,云原生基础架构不等于在容器中运行应用程序。当Netflix率先推出云原生基础架构时,几乎所有的应用程序都是用虚拟机镜像部署,而不是容器。打包应用程序的方式并不意味着将拥有自治系统的可扩展性和优势。即使应用程序是通过持续集成和持续交付流水线自动构建和部署的,也并不意味着可以从补充API驱动部署的基础架构...
- 下一篇
Android通知栏前台服务
Android通知栏前台服务一、前台服务的简单介绍前台服务是那些被认为用户知道且在系统内存不足的时候不允许系统杀死的服务。前台服务必须给状态栏提供一个通知,它被放到正在运行(Ongoing)标题之下——这就意味着通知只有在这个服务被终止或从前台主动移除通知后才能被解除。 最常见的表现形式就是音乐播放服务,应用程序后台运行时,用户可以通过通知栏,知道当前播放内容,并进行暂停、继续、切歌等相关操作。 二、为什么使用前台服务后台运行的Service系统优先级相对较低,当系统内存不足时,在后台运行的Service就有可能被回收,为了保持后台服务的正常运行及相关操作,可以选择将需要保持运行的Service设置为前台服务,从而使APP长时间处于后台或者关闭(进程未被清理)时,服务能够保持工作。 三.前台服务的详细使用创建服务内容,如下(四大组件不要忘记清单文件进行注册,否则启动会找不到服务);public class ForegroundService extends Service { private static final String TAG = ForegroundService.cla...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS8安装Docker,最新的服务器搭配容器使用
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- 设置Eclipse缩进为4个空格,增强代码规范
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- CentOS8编译安装MySQL8.0.19
- SpringBoot2整合Redis,开启缓存,提高访问速度
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- Hadoop3单机部署,实现最简伪集群
- CentOS7,CentOS8安装Elasticsearch6.8.6