!通过阅读Android开发艺术探索整理
存在的原因: Android UI 控件是非线程安全的,多线程并发访问会导致控件不可控,如果用锁会阻塞,降低访问效率,另外会让UI的访问变的复杂。因此选择单线程模型操作UI
相关概念:
Handler与Message/Looper/MessageQuene的关系:
Looper.perpare实例化当前线程对应的Looper及消息队列MessageQuene
Handler.post(Runnable)与Handler.sendMessage:
线程队列与消息队列
post通过sendMessageDelay()最终将getMessage(Runnable)取得的Message对象发送到消息队列
sendMessage本质最终是通过enqueueMessage将Message对象插入到消息队列
Looper.loop开启无限循环,通过消息队列内的循环next方法,不断从MessageQunene取出Message,通过msg.target.dispathMessage分发到Handler中,此处msg.target为发送该消息的Handler对象,由handleMessage处理
退出循环:Looper.quit或Looper.quitSafity
主线程的特殊性:
Looper.perpareMainLooper实例化Looper与消息队列
使用getMainLooper处理Message
通过Looper.loop实现主线程循环
ActivityThread.H:主线程Handler,内部定义四大组件启动停止消息类型
主线程消息循环:
主线程ActivityThread通过ApplicationThread与AcitivityManagerService进程间通信。AMS完成ActiviyThread请求后回调ApplicationThread内的Binder,向ActivityThread.H发送消息,H将ApplicationThread逻辑切换到ActivityThread执行,完成主线程消息循环
消息机制流程:
Looper.perpare实例化Looper对象与MessageQuene消息队列,子线程Handler对象通过postRunnable或sendMessage方法向消息队列内插入Message,Looper.loop方法调用MessageQuene的next方法查询并取出消息队列内的Message对象,dispathMessage分发给主线程的Handler,Handler对象调用handleMessage处理。
这里引申一个面试时遇到的问题:能否在主线程开启Looper.loop?
简单说一下我的思路.
1.Looper.loop方法采用死循环方式调用消息队列的next方法查询取出Message对象,除非取到Message对象,否则将一直阻塞
2.主线程防止anr规则是耗时任务放到子线程完成
3.主线程采用的是特殊的Looper对象,通过Looper.perpareMainLooper获取
4.猜想:若主线程可以调用Looper.loop,由于该方法为阻塞式方法,将有可能导致主线程anr;若不可以调用Looper.loop方法,那么主线程消息模型通过什么完成消息循环机制?那么这个问题的本质就是主线程如何保证loop时不产生anr?
下面提供一个知乎上的回答:https://www.zhihu.com/question/34652589,讲的还是比较详细。
我在这里简单做个针对该问题的整理
loop方法的死循环并非简单的死循环,在消息队列无消息时,会阻塞在消息队列next中的native方法里,底层是linux管道机制,此时主线程会释放CPU资源进入休眠,直到下一个消息到达,通过向pipe管道写入数据唤醒主线程工作。
真正在Activity中导致anr的是在onCreate/onStart/onResume 操作时间过长,而我们的Looper.loop方法是不会导致anr的。