1. 为什么需要Android的消息机制
Android规定访问UI只能在主线程中进行。若在子线程中访问UI就会抛出异常。这个验证由ViewRootImpl的checkThread()来完成。
为什么不允许在非主线程访问UI呢?这是因为Android的UI控件不是线程安全的。并且UI访问没有锁机制,并发访问会导致控件处于不可预期的状态。
那为什么不对UI访问加上锁机制呢?
(1)这显然会让UI访问的逻辑变得极其复杂;
(2)锁机制自然会降低效率;
(3)锁机制还会阻塞某些进程的执行。
但是Android又不建议在主线程进行耗时操作,因为这可能会引起ANR。那么需要经过时间处理的逻辑才能影响UI结果的情况该如何处理呢?Android的消息机制应运而生。本文原创,转载请注明出处:Android开发——Android的消息机制详解_SEU_Calvin的博客-CSDN博客
2. Android的消息机制结构
Android的消息机制主要是指Handler的运行机制。Handler的运行需要底层MessageQueue和Looper的支撑。
2.1 MessageQueue
MessageQueue采用以单链表为数据存储结构的消息列表。对外提供插入(enqueueMessage)和读取(next)工作。读取本身附带删除操作。单链表在插入和删除上比较有优势。
插入操作根据消息的延迟时间来进行调整插入位置;
读取操作是一个无限循环,如果消息队列中没有消息就会阻塞,当有新消息到来时,就返回这条消息并将其从单链表中删除。
2.2 Looper
2.2.1 Looper的作用
在Looper的构造方法中会创建一个消息队列并获得当前线程的对象。每一个线程都会维护自己的Looper,具体可以参考Android开发——ThreadLocal功能介绍。Handler在创建时会采用当前线程的Looper来构造消息循环系统。
loop()方法是一个死循环,会调用消息队列的next方法,next是一个阻塞操作,前面也讲过了。只有当next返回了新消息,Looper才会处理这条消息。这样Handler发送的消息最终又交给handler的dispatchMessage方法来处理,我们编写的UI更新操作就在Looper对象的代码中执行了,这个Looper运行在我们创建Handler时的线程中的,也就是主线程,那这样一来就成功的把代码执行从子线程切换到了主线程中。
跳出looper()死循环的唯一条件是MessageQueue的读取方法next()返回了null。当Looper的quit()方法被调用时,消息队列就会被标记为退出状态,它的next()也就返回了null。所以Looper必须退出,否则loop()方法会一直执行下去。
2.2.2 如何为一个线程创建Looper
(1)UI线程,主线程被创建时就会初始化Looper,因此在主线程默认可以使用Handler。
(2)子线程默认是没有Looper的,Handler创建前必须手动创建Looper,否则会报错。
通过Looper.prepare()即可为当前线程创建一个Looper,并通过Looper.loop()来开启消息循环。如下所示。
new Thread(){
@Override
public void run() {
Looper.prepare();
Handler handler = new Handler();
Looper.loop();
}
}.start();
(3)还有一种情况是使用HandlerThread,HandlerThread 继承自Thread,它和普通Thread不同的是其内部已经封装了Looper。并对外提供自己这个Looper对象的get方法。具体关于HandlerThread的介绍请查看Android开发——HandlerThread以及IntentService详解。
2.3 Handler
Handler主要工作是消息的发送和接收。
消息的发送可以使用Handler的post的方法(最终还是通过send方法完成)将一个Runnable投递到Looper中去处理。
handler.post(new Runnable(){
@Override
public void run() {
//do something
}});
或者这种用法的变形,用途很广,功能是延迟3秒后从欢迎界面进入主界面。这里并不是开启了一个新的线程。
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
Intent intent = new Intent(SplashActivity.this, MainActivity.class);
startActivity(intent);
finish();
}
}, 3000);
看一下handler.post(Runnable callback)方法的源码,很明显最终还是通过send方法完成的。
public final boolean post(Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
再看一下sendMessageDelayed的源码:
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
这里面有个关键就是方法getPostMessage(r)这个方法,将Runnable转成一个Message,他内部到底干了什么呢?看一下他的源码:
private final Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
这里面就是将Runnable转化成一个Message,其他看他的代码很简单,就是先获取一个空消息Message.obtain(),然后将Message中私有变量callback的值设置成Runnable。
send方法被调用时,会调用消息队列的enqueueMessage方法将这个消息放入消息队列中,然后next方法会返回这条消息给Looper,Looper发现新消息到来就把消息交给Handler处理。Handler的dispatchMessage方法被调用,过程如下。
public void dispatchMessage(Message msg){
if(msg.callback!=null){
handleCallback(msg);
}else{
if(mCallback!=null){
if(mCallback.handleMessge(msg)){
return;
}
}
handleMessage(msg);
}
}
(1)首先检查Message的callback,不为null就意味着callback是一个Runnable对象(实际上就是Handler的post方法所传递的Runnable参数)。然后就会通过handleCallback来处理消息。handleCallback逻辑很简单,直接就是msg.callback.run(),执行我们在Runnable方法里重写的run方法。
(2)其次检查mCallback不为空,调用mCallback的handleMessage方法来处理消息。
这是为了处理以下这种Handler的使用情况。CallBack可以用来创建一个Handler的实例但并不需要派生出Handler的子类。
Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
});
Callback是一个接口,在重写它的handlerMessage方法时,处理完消息返回true的话,dispatchMessage也就返回了,就不用再处理消息了。
(3)如果返回了false或者mCallback为空,才会执行Handler的handleMessage(msg)方法来处理消息。
这样我们就分析完了Android消息机制的整个流程。
最近看到一篇介绍Looper、Handler、MessageQueue,Message作用和意义的文章,挺好的,可以拿来深入理解一下。