Android Handler机制
Posted 冬天的毛毛雨
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android Handler机制相关的知识,希望对你有一定的参考价值。
Looper是整个跨线程通信的管理者
// 内部持有的变量如下:
ThreadLocal<Looper>
MainLooper
Observer
MessageQueue
Thread
1.首先先回忆一下Handler怎么用
Android线程通信分为以下两种情况
1.子线程发消息给UI线程
2.UI线程发消息给子线程
3.子线程发消息给另个子线程
1.子线程发消息给UI线程
class FragmentContentActivity : AppCompatActivity() {
val FLAG = 1
lateinit var handler: Handler
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
handler = object : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
when (msg.what) {
FLAG -> {
findViewById<TextView>(R.id.text).text = msg.data["Text"].toString()
}
}
}
}
thread {
Thread.sleep(2000L)
handler.sendMessage(Message.obtain().apply {
what = FLAG
data = Bundle().apply {
this.putString("Text", "ThreadMessage")
}
})
}
}
}
2.UI线程/子线程发消息给子线程
class FragmentContentActivity : AppCompatActivity() {
val THREAD_FLAG =2
lateinit var threadHandler: Handler
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
thread {
Looper.prepare()
threadHandler = object :Handler(Looper.myLooper()!!){
override fun handleMessage(msg: Message) {
when(msg.what){
THREAD_FLAG -> {
Toast.makeText(
this@FragmentContentActivity,
"${msg.data["Text"]}",
Toast.LENGTH_SHORT
).show()
}
}
}
}
Looper.loop()
}
}
override fun onResume() {
super.onResume()
findViewById<TextView>(R.id.text).postDelayed({
threadHandler.sendMessage(Message.obtain().apply {
what = THREAD_FLAG
data = Bundle().apply {
putString("Text","UI Message")
}
})
},2000L)
}
}
**在子线程的使用中,我们发现必须要进行Looper.prepare()和Looper.loop()前后这两个操作,因此,带着这个疑问来看一下Looper的逻辑
**
// 在调用prepare()之后一定要调用loop(),最后结束消息循环的时候调用quit()
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));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
prepare()就是将初始化一个Looper对象放入到ThreadLocal中,初始化Looper,同时mQueue
public static void loop(){
Binder.clearCallingIdentity()
for (;;) {
Message msg = queue.next(); // might block
long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
try {
// 其实 loop()只做了这一个调用,其他的都是监控当前消息循环时间是否超时,应该和ANR有关
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} catch (Exception exception) {
if (observer != null) {
observer.dispatchingThrewException(token, msg, exception);
}
throw exception;
} finally {
ThreadLocalWorkSource.restore(origWorkSource);
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (logSlowDelivery) {
if (slowDeliveryDetected) {
if ((dispatchStart - msg.when) <= 10) {
Slog.w(TAG, "Drained");
slowDeliveryDetected = false;
}
} else {
if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
msg)) {
// Once we write a slow delivery log, suppress until the queue drains.
slowDeliveryDetected = true;
}
}
}
if (logSlowDispatch) {
showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
}
//消息实体回收
msg.recycleUnchecked();
可以看到Looper.loop其实只是在for循环中,获取mQueue的下一个msg节点,然后调用msg.target.dispatchMessage(msg)。乍看只是msg对象内部的操作。
因为loop()其实逻辑上算死循环,这意味着,当前线程的自发的顺序执行命令到此结束了,只能通过其他线程触发handler机制,来被动的在当前线程执行命令,当前线程完全变成了一个响应线程
Looper类只是初始化并开启线程死循环的一个开关,具体工作在MessageQueue中进行
MessageQueue 消息队列
队列内消息的添加不是直接调用MessageQueue,而是由与Looper相关联的Handler调用
MessageQueue的内部持有的变量如下: ArrayList mMessages SparseArray IdleHandler[] mBlocked
MessageQueue类的功能主要有:元素插入队列,获取队列的头部元素,查找队列中元素,综述就是对队列的增删改查,其中 mMessage就是这个队列的入口也是这个队列的头结点
boolean enqueueMessage(Message msg,long when) //msg 元素插入队列
boolean hasMessages(Handler h,int what,Object object) //查找handler下的msg.what/object相同的Msg
boolean hasEqualMessages(Handler h,int what,Object obj)//查找 msg.object.equal(obj)的msg
removeMessages(Handler h,int what,Object obj)/(Handler h,Runnable r,Object obj)
removeEqualMessages(...) //删除与参数msg.object相同或equal的msg
Message next() //获取队列中的头部元素
可以看出,这些方法内部都调用了 synchronized(this),队列的操作都是线程同步的
Message next() ->
...
// linux机制下的总线进入轮询,线程相当于挂起状态,nextPollTimeOut是挂起多长时间
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
//先判断msg.target是否为null,表示当前消息是不是异步消息
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
//同步屏障:取出当前队列中的异步消息
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
//重新计算线程进入挂起状态的时间
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
...
可以看出next()内部主要有两种获取msg的逻辑
1.当前消息都是普通消息,按照msg.when的大小排序,每一次循环执行,通过检测when是否大于now来决定是否获取msg,或是挂起当前线程。
2.当前消息中有异步消息,优先获取msg.isAsynchronous()==true的,或者按照此异步消息的等待时间,来重新设置挂起线程的时间,从而达到精准的获取异步消息。
通俗的来讲就是说,当前所有普通消息按照预约的执行时间的先后来排队,这样可基本上既可以达到按照预约时间执行消息,也可以最大效率的在一定时间段内执行最多的消息,但是这忽略了每个消息的执行消耗的时间,比如A消息是队列内的No.1,A消息预约执行时间是1s之后,整个队列是等待状态的,这个时候来了B消息,B消息预约的时间是0.999s之后,按照预约时间的排队机制,B消息要插队到A消息之前,B成了这个队列的No.1,A成了No.2,整个队列的等待时间还是1s(因为之前设置了等待时间,所以不用唤醒),但是B消息的执行过程长达0.5s,已经超过了之后的很多消息的预约执行时间点了,这样就不能保证某些重要的消息按时执行。
于是就有了异步消息同步屏障的机制,这相当于普通消息排队时来了一个VIP消息,先按照预约时间找到自己的位置,然后大喝一声:“都把脚给我挪开,我的前面不允许有人”,这个时候排在他之前的普通消息就只能全部挪到队列的一边,然后队列重新按照这位VIP消息,设置等待时间,期间新来的普通消息也插到队边等待,保证精准按时执行VIP消息。等VIP消息执行完,之后再把之前等待普通消息的队列合并执行。当然之前等待的消息全耽误了,但毕竟是普通消息不重要。
// 同步屏障的方法,此方法只在 ViewRootImpl类中调用
private int postSyncBarrier(long when) {
// Enqueue a new sync barrier token.
// We don't need to wake the queue because the purpose of a barrier is to stall it.
synchronized (this) {
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
//没有设置target
Message prev = null;
Message p = mMessages;
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
//mMessages变为同步屏障消息,next()下一次循环,首先获取到的是同步屏障
mMessages = msg;
}
return token;
}
// ViewRootImpl
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
//设置同步屏障之后,通过设置了Aysnc标记位的Handler发送的Msg都是异步消息,
//MessageQueue也优先处理此类异步消息,直到移除同步屏障标记位,再恢复到普通消息队列。
由此可见,同步屏障的设置和View刷新机制有关,因为要保证Vsync信号按时完成刷新操作,具体分析待续…
综述,异步消息可以保证精准的执行,但也因此消息事件的先后顺序被打乱,有可能在代码执行中执行了Handler.sendMsg(1,time0.2)->AsyncHandler.sendMsg(2,time0.5),但是实际执行的是 2->1。
再看Handler
Handler的成员变量如下
mLooper :初始化时获取当前线程的Looper对象引用 mQueue :通过Looper.mQueue 获取到的MessageQueue队列引用mAsynchronous :标记当前Handler是否发送异步消息 mCallback : Handler自身的callback接口,此callback调用在Message.callback之前mMessenger :IMessager 和进程通信相关
以上成员变量大都是final类型,表示Handler也是在其使用上也是final类型,也就是说,没有办法通过将Handler与context的生命周期相剥离来避免内存泄漏
Handler的方法如下
//Handler 发送Message第一种方法,设置Message的what,data
//不设置 runnable:Callback
boolean sendMessage(Message msg) -> boolean sendMessageDelayed(Message msg,long delayTime)
-> boolean sendMessageAtTime(Message msg,SystemClock.uptimeMillis()+delayTime)
-> mQueue.enqueueMessage(msg,uptime)
//第二种方法,Message只设置runnable:Callback
boolean postAtTime(Runnable r,Object token,long uptime)
-> sendMessageAtTime(getPostMessage(r,token),uptime)
--> Message getPostMessage(Runnable r,Object token){
Message.obtain().callback=r
...
}
//移除Message和检验Message
removeMessages()
hasMessages()
...
//Message 回调执行
void dispatchMessage(Message msg){
if(msg.callback!=null){
handleCallback(msg) ->
}else{
if(mCallback!=null){
mCallback.handleMessage(msg)
}
handleMessage(msg)
}
//可以看到 Message的回调分为三个等级
//No.1 msg自身的callback
//No.2 Handler自身的mCallback成员变量,mCallback是final类型
//No.3 Handler的子类重载的handleMessage方法
Message
Message 实现了Parcelable接口,也就是说可以作为进程间通信的载体
Message成员变量如下
int what //Handler发送主体下的Message的消息码
int arg1 //低成本的参数传递
int arg2
Object obj //可以为空的token对象,一般在进程通信中用到
Bundle data //线程通信中常用的参数容器
Handler target //发送主体
Runnable callback //Message自身回调
Messenger replyTo //进程通信,一般在AMS中用到
------
// Message缓存池相关
Object sPoolSync = new Object() // 同步标记
Messsage next
static Message sPool
static int sPoolSize
Message方法如下
//可以看出这是一个非常巧妙的方法
static Message obtain(){
synchronized(sPoolsSync){
if(sPools!=null){
Message m= sPool;
sPool = m.next;
m.next = null;
sPoolSize--;
return m;
}
}
return new Message();
}
//主体上是一个带缓存池链表的同步工厂模式,同时也考虑到较多线程阻塞时
//可以直接声明初始化对象
//回收Message对象到缓存池链表
void recycleUnchecked(){
...参数=null
synchronized(sPoolSync){
if(sPoolSize < MAX_SIZE){
next = sPool;
sPools = this;
sPoolSize++;
}
}
}
以上是关于Android Handler机制的主要内容,如果未能解决你的问题,请参考以下文章
从Handler+Message+Looper源代码带你分析Android系统的消息处理机制
[Android源代码分析]Android消息机制,Handler,Message,Looper,MessageQueue