对于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重新梳理了一波,加深了一下印象,也查漏补缺了一次,主要还是要学习他他在架构方面的一些思想吧