对于Handler可谓是虐我千百遍,待她如初恋
,为啥呢,因为我并不是真的理解她,所以只能任她虐。
为了不再被虐(撕心裂肺的那种),一层一层剥开她的心,剥光了来看
先来几个问题(如果你都懂了,那么此文就没必要再往下读了)
开胃小菜
1.一个线程有多少个Handler?
2.一个线程有多少个Looper?如何保证的?
3.Handler内存泄漏的原因?为什么其他的内部类(recycleview、adapter、viewholder)没有提到过这种问题?
4.为什么主线程可以new Handler? 如果在子线程中new Handler需要怎么做?
5.子线程维护的Looper,消息队列无消息的时候的处理方案是什么?有什么用?
6.既然可以存在多个Handler往MessageQueue中添加数据(发消息时各个Handler可能处于不同线程)那么它内部是如何保证线程安全的?取消息呢?
7.我们使用Message时需要怎么创建它?
8.Looper死循环为什么不会导致应用卡死?
9.MessageQueue的数据结构是什么样的?为什么要用这个数据结构?
10.Handler是如何实现同步屏障的?
11.线程是如何进行切换的?
12.Handler用了什么设计模式?用它的好处是什么?
这些问题可能是面试过程中问的比较多的一些问题,也是我们学习Handler的时候必须要掌握的知识点,如果说还有疑问,可以从下文中找到对应的答案哦~
在开发过程中,经常需要在子线程中进行一些操作,然后操作完毕会通过Handler发送一些数据给主线程,通知主线程做相应的操作
Handler基础
-
相关类
Handler:发送和接收消息
Looper:用于轮询消息队列,一个线程只能有一个Looper
Message:消息实体
MessageQueue:消息队列用于存储消息和管理消息
-
使用
分为三步:
- 创建一个Handler
private Handler handler=new Handler(){
@Override
public void handleMessage(Message msg){
super.handleMessage(msg);
}
}
- 发送消息
Message message=handler.obtainMessage();
message.what=1; //what属性,int类型,主要是给主线程识别组线程发来的是什么消息
message.arg1=i; //arg属性, 如果消息是int类型,可以赋值给arg1,arg2参数
message.obj="handler test"; //obj属性,Object类型,将消息传递给obj
handler.sendMessage(message);
- 响应消息
private Handler handler=new Handler(){
@Override
public void handleMessage(Message msg){
super.handleMessage(msg);
//处理接收到的消息
int arg1=msg.arg1;
String info=(String)msg.obj;
}
}
正常来说,这样写就已经能正常使用了,但是Handler在子线程中也是能使用的,如果这样在子线程中使用,100%的报错
Handler源码
还记得上面说的如果在子线程中使用会报错吗?现在我们看下Handler的构造函数
public Handler() {
this(null, false);
}
/**
* @hide
*/
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
//创建Looper
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
从上面的源码中我们看到需要一个Looper,那么可能你会问,为什么在Activity中可以直接new Handler呢?我也没有添加Looper啊?
创建Looper
创建Looper的方法就是调用了Looper.prepare()方法
不知道你有没有注意过ActivityThread.java这个类,在这个类中的main方法有这样的代码
public static void main(String[] args){
// ....省略其他代码
Looper.prepareMainLooper();
ActivtyThread thread=new ActivityThread();
thread.attach(false);
//....
Looper.looper();
}
public static void prepareMainLooper(){
prepare(false); //消息队列不可以退出
synchronized(Looper.class){
if(sMianLooper!=null){
//....
}
sMainLooper=myLooper();
}
}
public static void prepare(){
prepare(true);//消息队列可以quit
}
Looper和Thread进行关联
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) { //不为空就表示当前线程已经创建了Looper
//异常提示每一个线程只能创建一个Looper
throw new RuntimeException("Only one Looper may be created per thread");
}
// //创建Looper并设置给sThreadLocal,确保调用get()方法的时候不为空
sThreadLocal.set(new Looper(quitAllowed));
}
perpare()有两个重载的方法,主要是看prepare(boolean quitAllowed)方法,quitAllowed用于在创建MessageQueue时标识消息队列是否可以销毁,主线程不可以被销毁
看到这里,你应该知道子线程中使用Handler的正确姿势了吧!
1. 创建一个Looper,调用Looper.prepare()方法
2. 我们可以创建一个HandlerThead,即创建一个包含Looper的对象
MessageQueue和Looper的绑定
private Looper(boolean quitAllowed){
// 创建了MessageQueue
mQueue= new MessageQueue(quitAllowed);
// 绑定当前的线程
mThread =Thread.currentThread();
}
MessageQueue的构造方法
MessageQueue(boolean quitAllowed){
//quitAllowed用于在创建MessageQueue时标识消息队列是否可以销毁,主线程不可以被销毁(false)
mQuitAllowed=quitAllowed();
mPtr=nativeInit();
}
Looper.loop()
代码就不上了,简单总结下
1. 调用sThreadLocal.get()获取获取刚创建的Looper对象
2. 判断Looper是否存在,如果为空就抛出异常
3. 创建一个for的死循环,从消息队列不断的取消息
4. 判断是否有消息,由于刚创建MessageQueue就已经开始轮询了,队列中是没有消息的,所以需要等到Handler sendMessage- euqueueMessage后才有消息
5. 获取到消息后通过msg.target绑定的Handler进行消息的分发
创建Handler
创建Handler的操作可以查看上文
创建Message
可以直接使用new Message的方式创建
通常使用Message.obtain,因为obtain可以检查是否有复用的Message,可以通过复用避免过多的创建、销毁Message对象达到优化内存和性能的目的
public static Message obtain(Handler h){
//调用重载的obtain方法
Message m=obtain();
m.target =h;
return m;
}
public static Message obtain(){
synchronized(sPoolSync){//sPoolSync是一个Object对象,用来保证线程安全
if(sPool!=null){//sPool就是Handler dispatchMessage后通过recycleUnchecked回收以复用的Message
Message m= sPool;
sPool= m.next;
m.flags=0; //清除使用标记
sPoolSize--;
return m;
}
}
return new Message();
}
Message和Handler的绑定
创建Message的时候已经通过Message.obtain(Handler h)这个构造方法进行了绑定,在Handler中的enqueueMessage()方法中也绑定了,所有发送Message的方法都会调用此方法入队,所以在创建Message的时候是可以不进行绑定的
private boolean enqueueMessage(MessageQueue queue,Message msg,long uptimeMillis){
msg.target=this;//进行绑定
if(mAsynchronous){
msg.setAsynchronous(true);
}
return queue.enqueuMessage(msg,uptimeMillis);
}
Handler消息(发送/处理)
Handler发送消息的重载方法有很多
sendMessage(Message msg)
sendMessageDelayed(Message msg, long uptimeMillis)
post(Runnable r)
postDelayed(Runnable r, long uptimeMillis)
sendMessageAtTime(Message msg,long when)
sendEmptyMessage(int what)
sendEmptyMessageDelayed(int what, long uptimeMillis)
sendEmptyMessageAtTime(int what, long when)
主要是sendMessage(Message)
,调用了sendMessageDelayed
继续调用sendMessageAtTime
,调用enqueueMessage
再继续调用到MessageQueue中的enqueueMessage
,将消息保存在了消息队列中,最终由Looper取出,交给Handler的dispatchMessage
进行处理
public void dispatchMessage(Message msg){
if(msg.callback!=null){ //callback在message的构造方法中初始化或者使用 handler.post(Runnable)时候才不为空
handlercallback(msg);
}else{
if(mCallback!=null){ //mCallback是一个Callback对象,通过无参的构造方法创建出来的Handler,该属性为null时不会进入到此判断
if(mCallback.handleMessage(msg)){
return ;
}
}
handleMessage(msg);
}
}
private static void handleMessage(Message msg){
msg.callback.run();
}
从dispatchMessage
方法,我们知道message中callback是一个Runnable对象,如果callback不为空,则直接调用callback的run方法,否则判断mCallback是否为空,mCallback在Handler构造方法中初始化,在主线程中直接通过无参的构造方法new出来的为null,所以会执行后面的handlemessage
方法
在handlemessage
中可以拿到Message对象,根据不同的需求进行处理,到此Handler算是结束了
Handler小结
handler.
sendMessage
发送消息到消息队列MessageQueue,然后Looper通过loop()
函数轮询MessageQueue中的Message,当Message到了可执行的时候开始执行,执行后就会调用message绑定的Handler进行处理
再探Handler
从设计思想看Handler
子线程发送消息,主线程处理消息,构成了线程模型中的经典问题: 生产者-消费者模式(内存共享)
生产者-消费者模式:主要让生产者和消费在同一时间段内共用同一个存储空间,生产者往存储空间中添加数据,消费者从存储空间中取出数据
这样做的好处是什么?
可以保证数据生产消费的顺序(MessageQueue先进先出)
不管是生产者(子线程)还是消费者(主线程)都只依赖缓冲区(Handler)
,不会相互持有,没有任何耦合
MessageQueue
简单的说:
MessageQueue是一个消息队列,Handler将Message发送到消息队列中,消息队列会按照一定的规则取出要执行的Message
- 成员变量
// True if the message queue can be quit.
//用于标示消息队列是否可以被关闭,主线程的消息队列不可关闭
private final boolean mQuitAllowed;
@SuppressWarnings("unused")
// 该变量用于保存native代码中的MessageQueue的指针
private long mPtr; // used by native code
//在MessageQueue中,所有的Message是以链表的形式组织在一起的,该变量保存了链表的第一个元素,也可以说它就是链表的本身
Message mMessages;
//当Handler线程处于空闲状态的时候(MessageQueue没有其他Message时),可以利用它来处理一些事物,该变量就是用于保存这些空闲时候要处理的事务
private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
// 注册FileDescriptor以及感兴趣的Events,例如文件输入、输出和错误,设置回调函数,最后
// 调用nativeSetFileDescriptorEvent注册到C++层中,
// 当产生相应事件时,由C++层调用Java的DispathEvents,激活相应的回调函数
private SparseArray<FileDescriptorRecord> mFileDescriptorRecords;
// 用于保存将要被执行的IdleHandler
private IdleHandler[] mPendingIdleHandlers;
//标示MessageQueue是否正在关闭。
private boolean mQuitting;
// Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout.
// 标示 MessageQueue是否阻塞
private boolean mBlocked;
// The next barrier token.
// Barriers are indicated by messages with a null target whose arg1 field carries the token.
// 在MessageQueue里面有一个概念叫做障栅,它用于拦截同步的Message,阻止这些消息被执行,
// 只有异步Message才会放行。障栅本身也是一个Message,只是它的target为null并且arg1用于区分不同的障栅,
// 所以该变量就是用于不断累加生成不同的障栅。
private int mNextBarrierToken;
- 数据是有序的吗? 你这不是废话吗?肯定有序啊,来来来,看源码
boolean enqueueMessage(Message msg, long when) {
// ....
synchronized (this) {
// ....
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
// 一致循环,直到找到尾巴(p == null)
// 或者这个 message 的 when 小于我们当前这个 message 的 when
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
}
return true;
}
Queue 都是有序的,Set 才是无序的,它的排序的依据是通过when
字段,表示一个相对时间,该值是由 MessageQueue#enqueueMessage(Message, Long) 方法设置的
- when是怎么来的? Message#when 是一个时间,用于表示 Message 期望被分发的时间,该值是 SystemClock#uptimeMillis() 与 delayMillis 之和
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
既然是时间,为什么不直接用System.currentTimeMillis()
???
return sendMessageAtTime(msg, System.currentTimeMillis()+ delayMillis);
为什么不用呢?首先我们先了解下这两者的区别
SystemClock.uptimeMillis() 是一个表示当前时间的一个相对时间,它代表的是 自系统启动开始从0开始的到调用该方法时相差的毫秒数
System.currentTimeMillis() 代表的是从 1970-01-01 00:00:00 到当前时间的毫秒数,我们可以通过修改系统时间达到修改该值的目的,所以该值是不可靠的值
看完解释,一目了然了吧!
在上面的成员变量中,看到了IdleHandler这个东东,虽然用的少,但也简单的了解下吧
IdleHandler
IdleHandler在处理业务逻辑方面和Handler一样,不过它只会在线程空闲的时候才执行业务逻辑的处理,这些业务经常是哪些不是很紧要或者不可预期的,比如GC
从源码上来看,我们发现这货就是一个接口而已,内部就一个带返回值的方法boolean queueIdle(),在使用的时候只需要实现该接口并加入到MessageQueue中就可以了
public static interface IdleHandler {
boolean queueIdle();
}
- 使用
MessageQueue messageQueue = Looper.myQueue();
messageQueue.addIdleHandler(new MessageQueue.IdleHandler() {
@Override
public boolean queueIdle() {
// do something.
return false;
}
});
public void addIdleHandler(@NonNull IdleHandler handler) {
//非空判断
if (handler == null) {
throw new NullPointerException("Can't add a null IdleHandler");
}
//加一个同步锁
synchronized (this) {
//调用mIdleHandlers.add(handler)添加
mIdleHandlers.add(handler);
}
}
/**
*从消息队列中移除一个之前添加的IdleHandler。如果该IdleHandler不存在,则什么也不做
*/
public void removeIdleHandler(@NonNull IdleHandler handler) {
synchronized (this) {
mIdleHandlers.remove(handler);
}
}
ThreadLocal(核心成员)
我们知道Handler利用了内存共享
的原理,那么ThreadLocal就是最大的功臣
ThreadLocal设计的初衷是提供线程内部的局部变量,在本地线程内随时随地可取,隔离其他线程
-
类图
-
结构图
-
set操作
public void set(T value) {
//先拿到保存键值对的ThreadLocalMap对象实例map
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
//
if (map != null)
map.set(this, value);
else
//如果map为空(即第一次调用的时候map值为null),去创建一个ThreadLocalMap对象并赋值给map,并把键值对保存在map
createMap(t, value);
}
getMap实现非常直接,就是直接返回Thread对象的threadLocal字段
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
//Thread.java
ThreadLocal.ThreadLocalMap threadLocals = null;
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
简单的说每个线程引用的ThreadLocal副本值都是保存在当前Thread对象里面的。存储结构为ThreadLocalMap类型,ThreadLocalMap保存的类型为ThreadLocal,值为副本值
- get操作
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
和set方法原理一样,拿到当前线程Thread对象实例中保存的ThreadLocalMap对象map,然后从map中读取键为this(即ThreadLocal类实例)对应的值
从上面的图中我们发现 ThreadLocal内部有一个ThreadLocalMap对象,该Map对象里面维护了一个弱引用的Entry实体类
- ThreadLocalMap(内部类)
从源码中的文档上我们得知:
ThreadLocalMap是一个适用于维护线程本地值的自定义哈希映射(hash map),没有任何操作可以让它超出ThreadLocal这个类的范围。该类是私有的,允许在Thread类中声明字段。为了更好的帮助处理常使用的,hash表条目使用了WeakReferences的键。但是,由于不实用引用队列,所以,只有在表空间不足的情况下,才会保留已经删除的条目
- 存储结构 ThreadLocalMap中定义了额Entry数据实例table,用于存储Entry
private Entry[] table;
也就是说ThreadLocalMap维护一张哈希表(一个数组),表里存储Entry。既然是哈希表,那肯定会涉及到加载因子,即当表里面存储的对象达到容量的多少百分比的时候需要扩容
具体可参考HashMap原理
线程同步安全
Handler是用于线程间通信的,但是它产生的根本原因并不只是用于UI处理的,而更多的是Handler是整个App通信的框架,从ActivityThread.java
类中就已经感受到了,整个APP都是用它进行线程间的协调,既然如此重要,那么它又是如何保证自己的线程安全的呢?
Handler中至关重要的类MessageQueue
做了两件事,消息入库enqueueMessage
和消息出库next
,所以我们就从这个地方入手
我们发现不管是enqueueMessage()
还是next()
有个锁的存在
synchronized(this)
这个锁说明对调用同一个MessageQueue对象的线程来说,它们都是互斥的
在Handler里面,一个线程对应一个唯一的Looper对象,而Looper中只会有一个唯一的MessageQueue。所以也就是说我们主线程中只有一个MessageQueue对象,所有的子线程向主线程发送消息的时候,主线程一直只会处理一个消息,其他的都需要等待,那么这个时候消息队列就不会出现混乱
在next方法为什么要加锁?我每次从线程里面取消息,而且每次都是队列的头部,为什么还需要加锁?
在next方法中加锁,因为synchronized(this)的范围是所有this正在访问的代码块都受到保护,可以保证enqueueMessage()和next()函数能够实现互斥,这样才能症状的保证在多线程中访问的时候MessageQueue有序进行
同步屏障
举个例子
在马路上各种车都需要遵守交通信息,然后车辆依次通过,但是这个时间救护车正在执行任务的时候,交警就会指挥交通,让各种私家车辆让开一条绿色通道给救护车通过,待到救护车通过后,大家继续依次排队通过
我们知道Handler通常在没有设置的时候都是同步的,我们知道Handler的MessageQueue消息都是按照时间的先后顺序进行排序的,那么假如同一个时刻中某一个消息需要立即执行怎么办?
这个时候就涉及到同步屏障
了,也就是绿色通道
同步屏障:就是阻碍同步消息,只让异步消息通过
,那么如何开启同步屏障呢?
要想发送异步消息,那么我们需要满足target==null的条件
- target是个啥? 首先从源码中看到target是在Message类中定义的
//Message.java
Handler target;
也就是说Message是持有Handler的,而target就是Handler对象
- 异步消息
我们知道在Handler调用
sendMessage()
方法最终会进入到Handler的enqueueMessage()
让消息入队,但是这样入队的消息都有一个msg.target=this
,也就说这样的消息是一个同步消息
那么我们只需要满足target==null这个条件就可以达到发送异步消息的目的了
- 异步方式
1.直接在new Handler的设置
Handler handler=new Handler(true);
@hide
public Handler(boolean async){
this(null,async);
}
不过这个构造被标记为了@hide,一般不使用
2.将消息设置为异步
Message msg=myHandler.obtainMessage();
//设置异步消息
msg.setAsynchronous(true);
- 开启屏障方法
MessageQueue#postSyncBarrier()
简单查看源码发现在postSyncBarrier()
中初始化Message对象的时候并没有设置target,因此target==null,这样一条异步消息就插入到了消息队列
开启同步屏障后,那异步消息又是怎么处理的?
我们知道Handler的消息最终都是在Looper的loop()
方法中进行处理,而loop()
方法会循环调用MessageQueue的next()
从消息队列中进行获取消息
我们需要注意的关键代码是msg.traget==null
这一部分,也就说如果target为null的时候它就是屏障了,需要循环遍历,一直往后找到第一个异步消息
if(msg!=null && msg.target==null){
do{
perMsg=msg;
msg=msg.next;
}while(msg!=null&& !msg.isAsynchronous());
}
简单的说,当消息队列开启了同步屏障的时候,消息机制在处理消息的时候,优化处理异步消息,这样,同步屏障起到了一种过滤和优先级的作用
那么其他的同步消息什么时候可以进行处理呢?与添加同步屏障相反,需要移除屏障后我们才可以处理同步消息,即调用removeSyncBarrier()
- 使用场景 貌似在开发的过程中,我们很少用到同步屏障,那么源码中在哪里用到了?
Android中的UI消息就是异步消息,需要优先处理
比如View更新,调用onDraw
、requestLayout
、invalidate
等view最终都会调用到ViewRootImpl类中的scheduleTraversals
方法
void scheduleTraversals(){
if(!mTraversalScheduled){
mTraversalScheduled=true;
//开启同步屏障
mTraversalBarrier=mHandler.getLooper().getQueue().postSyncBarrier();
//发送异步消息
mChoreographer.postCallback();
...
}
}
postCallback会进入到Choreographer#postCallbackDelayedInternal方法,在此处发送异步消息
//简略些许
private void postCallbackDelayedInternal(){
//设置异步消息
msg.setAsynchronous(true);
}
这里开启了同步屏障,并发送了异步消息,由于UI更显相关的消息是优先级最高的,这样系统就会优先处理这些异步消息了。
当然处理完消息后要移除同步屏障,这个时候就调用到了ViewRootImpl#unscheduleTraversals()
清盘行动
- 一个线程有多少个Handler?
无数个,只要new Handler就有一个
2.一个线程有多少个Looper?如何保证的?
1个
在Looper.java有一个属性
static final ThreadLocal<Looper> sThreadLocal=new ThreadLocal<Looper>();
ThreadLocal中存放了一个ThreadLocalMap<this,value> ,this就是唯一的ThreadLocal,那么如何保证唯一呢?
在Looper的prepare()方法中先判断了sThreadLocal.get()方法进行一次判断
3.Handler内存泄漏的原因?为什么其他的内部类(recycleview、adapter、viewholder)没有提到过这种问题?
Handler是一个匿名内部类,默认就持有了外部类的对象,我们可以通过使用static或者弱引用类型就可以避免
和生命周期有关
sendMessage
enquenuMessage(msg.target=this)//持有了外部的引用,Message无法被回收,GC不可达
4.为什么主线程可以new Handler? 如果在子线程中new Handler需要怎么做?
创建Handler就需要new Handler,主线程已经实现了Looper的prepare()和loop()
子线程中创建方式:
1. 创建一个Looper,调用Looper.prepare()和Looper.loop()方法
2. 我们可以创建一个HandlerThead,即创建一个包含Looper的对象
5.子线程维护的Looper,消息队列无消息的时候的处理方案是什么?有什么用?
主线程无消息的时候,休眠阻塞
子线程中必须要调用Looper.quit()退出
loop是一个死循环的操作,要让loop退出,需要msg==null,这个时候就需要返回一个空的消息
quit()的作用
唤醒线程
将mquitting=true,让messagequeue==null,退出loop()
6.既然可以存在多个Handler往MessageQueue中添加数据(发消息时各个Handler可能处于不同线程)那么它内部是如何保证线程安全的?取消息呢?
加锁
enqueueMessage方法中使用 synchronized(this) 同步锁(内置锁),锁定了该对象
不同的线程发消息的时候,插入的数据进行排序操作
取消息的时候也需要一个加锁的操作,以防在取的时候有插入数据的操作导致数据错乱
7.我们使用Message时需要怎么创建它?
1.可以直接使用new Message的方式创建
2.一般来说,通常使用Message.obtain,因为obtain可以检查是否有复用的Message,可以通过复用避免过多的创建、销毁Message对象达到优化内存和性能的目的
8.Looper死循环为什么不会导致应用卡死?
真正卡死主线程操作的是在回调方法onCreate、onStart、onResume等操作时间过长,会导致掉帧甚至ANR
Looper.loop()本身不会导致应用卡死
9.MessageQueue的数据结构是什么样的?为什么要用这个数据结构?
请参考HashMap数据结构
10.Handler是如何实现同步屏障的?
MessageQueue#postSyncBarrier()
11.线程是如何进行切换的?
调用Looper的loop()方法无限循环查询MessageQueue队列是否有消息保存了。有消息就取出来调用dispatchMessage()方法处理。
这个方法最终调用了我们自己重写了消息处理方法handleMessage(msg);这样就完成消息从子线程到主线程的切换
12.Handler用了什么设计模式?用它的好处是什么?
生产者-消费者模式 内存共享的设计
好处:可以保证数据生产消费的顺序(MessageQueue先进先出)不管是生产者(子线程)还是消费者(主线程)都只依赖缓冲区(Handler),不会相互持有,没有任何耦合
总结
啰里啰嗦的说了这么多,对Handler重新梳理了一波,加深了一下印象,也查漏补缺了一次,主要还是要学习他他在架构方面的一些思想吧