《Android开发艺术探索》第十章重点笔记
第十章 Android消息机制
##10.1、Android消息机制概述
系统为什么不允许在子线程中去访问UI呢? 因为Android的UI控件不是线程安全的,多线程并发访问可能会导致UI控件处于不可预期的状态,为什么不加锁?因为加锁机制会让UI访问逻辑变得复杂;其次锁机制会降低UI访问的效率,因为锁机制会阻塞某些线程的执行。所以Android采用了高效的单线程模型来处理UI操作。
##10.2、消息队列的工作原理
消息队列指的是MessageQueue,主要包含两个操作:插入和读取。读取操作本身会伴随着删除操作。MessageQueue内部通过一个单链表的数据结构来维护消息列表,这种数据结构在插入和删除上的性能比较有优势。
插入和读取对应的方法分别是:enqueueMessage和next方法。
enqueueMessage()的源码实现主要操作就是单链表的插入操作
next()的源码实现也是从单链表中取出一个元素的操作,next()方法是一个无线循环的方法,如果消息队列中没有消息,那么next方法会一直阻塞在这里。当有新消息到来时,next()方法会返回这条消息并将其从单链表中移除。
##10.3、Looper的工作原理
Looper在Android的消息机制中扮演着消息循环的角色,具体来说就是它会不停地从MessageQueue中查看是否有新消息,如果有新消息就会立即处理,否则就一直阻塞在那里。
通过Looper.prepare()方法即可为当前线程创建一个Looper,再通过Looper.loop()开启消息循环。prepareMainLooper()方法主要给主线程也就是ActivityThread创建Looper使用的,本质也是通过prepare方法实现的。
Looper提供quit和quitSafely来退出一个Looper,区别在于quit会直接退出Looper,而quitSafely会把消息队列中已有的消息处理完毕后才安全地退出。 Looper退出后,这时候通过Handler发送的消息会失败,Handler的send方法会返回false。
在子线程中,如果手动为其创建了Looper,在所有事情做完后,应该调用Looper的quit方法来终止消息循环,否则这个子线程就会一直处于等待状态;而如果退出了Looper以后,这个线程就会立刻终止,因此建议不需要的时候终止Looper。
loop()方法会调用MessageQueue的next()方法来获取新消息,而next是是一个阻塞操作,但没有信息时,next方法会一直阻塞在那里,这也导致loop方法一直阻塞在那里。如果MessageQueue的next方法返回了新消息,Looper就会处理这条消息:msg.target.dispatchMessage(msg),这里的msg.target是发送这条消息的Handler对象,这样Handler发送的消息最终又交给Handler来处理了。
##10.4、Handler的工作原理
Handler的工作主要包含消息的发送和接收过程。通过post的一系列方法和send的一系列方法来实现。
Handler发送过程仅仅是向消息队列中插入了一条消息。MessageQueue的next方法就会返回这条消息给Looper,Looper拿到这条消息就开始处理,最终消息会交给Handler的dispatchMessage()来处理,这时Handler就进入了处理消息的阶段。
Handler的构造方法
1、派生Handler的子类
2、通过Callback
Handler handler = new Handler(callback)
其中,callback接口定义如下
public interface Callback{
public boolean handleMessage(Message msg);
}
3、通过Looper
public Handler(Looper looper){
this(looper,null,false);
}
10.5、 主线程的消息循环
Android的主线程就是ActivityThread,主线程的入口方法为main(String[] args),在main方法中系统会通过Looper.prepareMainLooper()来创建主线程的Looper以及MessageQueue,并通过Looper.loop()来开启主线程的消息循环。
ActivityThread通过ApplicationThread和AMS进行进程间通信,AMS以进程间通信的方式完成ActivityThread的请求后会回调ApplicationThread中的Binder方法,然后ApplicationThread会向H发送消息,H收到消息后会将ApplicationThread中的逻辑切换到ActivityTread中去执行,即切换到主线程中去执行。四大组件的启动过程基本上都是这个流程。
Looper.loop(),这里是一个死循环,如果主线程的Looper终止,则应用程序会抛出异常。那么问题来了,既然主线程卡在这里了,(1)那Activity为什么还能启动;(2)点击一个按钮仍然可以响应?
问题1:startActivity的时候,会向AMS(ActivityManagerService)发一个跨进程请求(AMS运行在系统进程中),之后AMS启动对应的Activity;AMS也需要调用App中Activity的生命周期方法(不同进程不可直接调用),AMS会发送跨进程请求,然后由App的ActivityThread中的ApplicationThread会来处理,ApplicationThread会通过主线程线程的Handler将执行逻辑切换到主线程。重点来了,主线程的Handler把消息添加到了MessageQueue,Looper.loop会拿到该消息,并在主线程中执行。这就解释了为什么主线程的Looper是个死循环,而Activity还能启动,因为四大组件的生命周期都是以消息的形式通过UI线程的Handler发送,由UI线程的Looper执行的。
问题2:和问题1原理一样,点击一个按钮最终都是由系统发消息来进行的,都经过了Looper.loop()处理。 问题2详细分析请看原书作者的Android中MotionEvent的来源和ViewRootImpl。
更多相关文章
- android的intent使用方法
- Android探索之路(一)——消息处理机制
- 在Android设备上搭建Web服务器的方法
- Android消息机制的源码理解
- Android setTag方法的key问题
- Android RSA与Java RSA加密不同标准产生问题的解决方法
- android截屏代码实现方法