type
status
date
slug
summary
tags
category
icon
password
Handler.post(runnable)
vs Handler.sendMessage(Message msg)
可以看到其实两种方式最终都是利用的
sendMessageDelayed
方法.区别是传递的Message对象.Handler.post(Runnbale runnable)
它的Message中callback为runnable
Handler.sendMessage(Message msg)
一般传递给它的message并不会指定callback,也就是说callback为null
那么Message是否带callback有何影响?这里要说一下Message最终分发的逻辑:
也就是一个执行优先级的区别了.
Handler与线程
ActivityThread
中已经为主线程准备好了Looper,即已经创建了Looper,并开启了循环:这就相当于为主线程暴露了一个消息传递的入口,在其他线程里可以创建一个主线程Handler,向主线程中传递消息,或者切换到主线程执行任务.
既然主线程可以这么做,其他线程也可以这么做,如果需要为线程开启消息循环机制,需要两步:
对于主线程之外的线程的消息传递机制可以参考一下
HandlerThread
的实现方式,也可以直接使用HandlerThread
:子线程可以被销毁,但是主线程在APP生命周期中一直存在着,因此主线程的Looper其实是不允许停止的:
Message 与 MessageQueue
Message本身的对象池复用不必多少,需要说的是Message对象实际上是一个单链表,Message中的next字段就是下一个Message节点.
我们都知道LinkedList可以当作一个队列使用,这里Message其实就是一个链表,自然也可以当作一个队列使用.
而MessageQueue则封装了Message的入队出队操作,还需要注意的是这并不是完全意义上的先进先出队列,因为Message中存在延时时间,所以入队的时候需要根据执行时间插入到指定的节点.
既然大体上还算是一个队列,那么肯定有一个入队和出队的过程:
- 入队:
- 出队:
这里既然是一个消息循环,那么应该是循环开始的时候会不断从队列中取出Message,这里是Looper.loop():
这里其实用的MessageQueue的next方法取出消息的:
其实也是一个根据时间查找节点的过程.
再补充一点,MessageQueue实际上是在Looper实例化的时候创建的,并且Looper持有了MessageQueue的引用,Handler中使用的就是Looper传递过去的MessageQueue.
开发者可以使用
Looper.myQueue()
获取对应的MessageQueue对象.IdleHandler
IdleHandler
时MessageQueue中的一个内部接口.当消息队列阻塞时,即消息为空,或当前没有要执行的消息的时候就会执行这个IdleHandler.一般添加
IdleHandler
都是使用:一般用在性能优化场景和一些不抢占主线程任务资源但是也有机会执行的场景:
- ActivityThread中的GC操作
- LeakCanary中内存泄漏提示Toast的显示
- Android的严格模式
- 各种Instrumentation
同步消息/异步消息/SyncBarrier(同步屏障)
Message 中可以通过调用
setAsynchronous()
方法来设置是否为异步消息.实际使用的时候,需要使用MessageCompat
.同步消息和异步消息的发送其实都是通过
sendMessageAtTime
发送的,这之后都会执行到:而SyncBarrier则是在MessageQueue中通过
postSyncBarrier(long when)
添加的:可以看到,这里的Message实际上没有给target赋值,因此target = null.也就是说SyncBarrier是target = null的Message.
当loop的时候如果遇到SyncBarrier,则会去寻找下一个异步消息,因此这里SyncBarrier的作用是拦截同步消息,通过异步消息.
在MessageQueue的next方法中有这么一段就是处理这部分逻辑的:
SyncBarrier一般都是成对使用的:
private int postSyncBarrier(long when)
public void removeSyncBarrier(int token)
不过目前这两个方法在应用层都无法使用.具体的使用场景可以参考
ViewRootImpl
:内存泄漏问题
Handler的内存泄漏问题是一个老生常谈的问题了,这里就不多说了,一般的场景有:
- 内部类/匿名内部类
内部类/匿名内部类会隐私持有外部对象,容易造成内存泄漏,因此需要使用静态内部类
- Callback问题
在Activity销毁或者任务执行完之后需要removeCallback,不然也有可能会导致内存泄漏
为什么主线程不会因为Looper.loop()而阻塞?
前面的loop循环有说过,里面有一个无限循环,但是为什么不会因此阻塞呢,导致无法处理其他的主线程任务.
这里需要明确一点:
- 主线程任务都会通过Message消息传递机制执行,也就是说最终都会在loop()中获取到并执行
Android Activity/Service的各种回调,比如onCreate()/onResume()等都是ActivityThread中H中有对应的消息类型进行处理,其实都是在消息队列中,并不会有什么其他类型的主线程任务会有别的方式去执行.
但是loop()中的无限循环,会很耗费资源吗?当没有任务时,是简单的sleep吗,那是如何唤醒的呢?
关键之处就在MessageQueue中next方法中的一个native方法调用:
在
android_os_MessageQueue.cpp
中有:可以看到这里其实用到的是Native层的
Looper
的poolOnce
方法.Native层的
Looper
支持监控文件描述符事件,还支持callback,并且其内部使用的是epoll()
方式.在
Looper.cpp
中有:关于epoll机制,可以查看https://zh.wikipedia.org/wiki/Epoll
- 作者:姜康
- 链接:https://jiangkang.tech/article/8c235d3a-26f6-48e7-96e9-a838b0ab8ac9
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。