您的位置:首页 > 移动开发 > Android开发

Android4.4KK中BOOT_COMPLETED广播问题的分析

2015-06-01 15:59 375 查看
详细内容和具体分析流程也可以下载PDF文档:



BOOT_COMPLETED issue analysis report


一、问题现象

手机开机后,按POWER键无法关闭屏幕,过了很长一段时间(20s左右)才能恢复正常。

Platform:MT6581
Android版本:4.4KK
BuildType:user
系统软件版本:SWC1I+ZZ
系统RAM:512M

二、Android4.4的POWER键处理流程

Android中对于输入事件的获取和分发分别由InputManagerService的InputReader以及InputDispatcher两个线程负责,按键属于输入事件的一种。
InputReader首先通过EventHub去获取各种类型的输入事件,当获取到之后会先通知InputDispatcher,并在进入输入事件队列之前先进行系统策略上的一些拦截,对于POWER键这样的特殊按键会进行提前的特殊处理,下图为具体的POWER按键处理流程:





三、问题初步分析

通过问题现象、log以及POWER键的处理过程,发现POWER键的处理流程之所以没有正常进行,是因为PowerManagerService没有及时获取到BOOT_COMPLETED广播,从而导致其相关状态没有被更新,关键代码为goToSleepNoUpdateLocked函数中关于判断当前状态以决定是否继续处理POWER键上下流程的代码,其中有关于mBootCompleted状态的判断,具体代码如下:



mBootCompleted状态开机默认为false,在收到BOOT_COMPLETED广播之后更新为ture,PowerManagerService接收BOOT_COMPLETED并更新mBootCompleted状态的关键代码如下:











既然mBootCompleted没有被及时更新,那么就说明BOOT_COMPLETED广播没有被及时收到,没有收到的话就要看为什么BOOT_COMPLETED广播没有被及时发出。

四、Android4.4上Broadcast(广播)的注册以及发送流程

广播(Broadcast)是一种在组件之间进行消息传递的方式,这些组件可以在相同或者不同的进程中,整个机制是在Binder进程间通信的基础上实现的。
广播机制存在一个注册调度中心,就是AcitivityManagerService。广播接收者订阅消息的形式就是将自己注册到ActivityManagerService中,并且指定要接收的广播类型。当广播发送者发送一个广播时,会首先发送到ActivityManagerService中,然后ActivityManagerService根据这个广播类型找到相应的广播接收者,最后将这个广播发送给它们。
广播接收者的注册方式分为静态注册和动态注册两种。静态注册是通过在配置文件AndroidManifest.xml中声明所感兴趣的广播的类型,以便ActivityManagerService能够找到它们。动态注册时在程序的代码中调用指定的接口将广播接收者注册到ActivityManagerService中。在同等情况下动态注册的广播接收者会优先收到广播。
广播的发送方式分为有序和无序两种。我们在注册广播接收者时可以指定它们的优先级,当ActivityManagerService接收到有序广播时,它就会将这个有序广播发送给符合条件的、优先级较高的广播接收者处理,然后再发送给符合条件、优先较低的广播接收者处理。当ActivityManagerService接收到无序广播时,它就会忽略广播接收者的优先级,先发送给动态注册的广播接收者,然后再将静态注册的广播接收者放入有序广播的处理队列中进行处理,所以对于无序广播动态注册的接收者总是优先接收到广播。
由于本文分析的问题出在广播的发送过程中,所以这里着重介绍广播的发送过程,以下为KK4.4上ActivityManagerService中finishBooting函数发出BOOT_COMPLETED广播的关键代码:



从上图的代码中可以看到红色框住的参数,此参数为true的话即以有序的方式发送广播,false即为无序方式。具体的广播发送过程如下:
1、广播发送者将一个特定类型的广播发送给ActivityManagerService(这里是ActivityManagerService直接发送BOOT_COMPLETED广播)
2、ActivityManagerService接收到一个广播之后,首先找到与此广播对应的广播接收者,然后根据广播的类型区分有序还是无序,有序广播会将所有的广播接收者无论静态还是动态注册都按照优先级进行排序放置到一个列表中,然后加入到有序广播的调度队列中。无序广播会先将动态注册的广播接收者放置到一个列表中,然后加入到无序广播的调度队列并向ActivityManagerService所运行的线程的消息队列发送一个消息,最后会将静态注册的广播接收者放置到另外一个列表中,然后加入到有序广播的调度队列中,所以静态注册的广播接收者总是会按照有序的方式进行处理,然后同样会向ActivityManagerService所运行的线程的消息队列发送一个处理广播的消息。这个时候对于广播发送者来讲,一个广播就发送完成了。
3、当ActivityManagerService所运行的线程的消息队列中的处理广播的消息被处理时,ActivityManagerService就会从广播调度队列中找到需要接收广播的广播接收者,并将对应的广播发送给他们所运行在应用程序进程。在这里需要特别说明的是ActivityManagerService对于无序广播的动态注册的广播接收者没有处理时间以及顺序上的限制,不会主动产生ANR,只是异步的将广播发送给接收者处理就返回。而对于有序广播则会严格限制时间和处理顺序,
4、广播接收者所在的应用程序进程接收到ActivityManagerService发送过来的广播之后,并不是直接调用广播接收者进行处理,而是将接收到的广播封装成一个消息,发送到主线程的消息队列中。当这个消息被处理时,应用程序进程才会将它所描述的广播发送给相应的广播接收者处理。
发送过程中各个组件的调用关系如下图:





以上是对无序广播的动态注册的广播接收者的发送过程,对静态注册的无序广播接收者以及有序广播的动态和静态注册的广播接收者的发送过程则比上面更复杂一点,牵扯到发送超时机制、有序同步反馈机制、启动静态广播接收者所在进程的机制等,大致的原理及过程如下:
1、对无序以及有序广播的静态广播接收者的调用过程需要先判断其所附属的进程是否存在,如果不存在需要先启动其所附属的进程,然后进程启动之后就将广播发送给其所在的应用进程,其所在的进程接收到广播之后则继续分发给具体的接收者,当广播接收者执行完毕之后需要将结果反馈给ActivityManagerService,以便让ActivityManagerService继续将广播分发下一个接收者,如果在分发一个广播给接收者的过程中超时了则会产生ANR。
2、对有序广播的动态注册的广播接收者的分发过程与静态类似,只是少了启动应用进程的过程,同样有分发完成的同步反馈以及超时无响应的ANR机制。

五、进一步的分析问题

通过测试发现Android4.2不存在此问题,因此对比KK4.4以及JB4.2上同样的代码发现在发送BOOT_COMPLETED广播时有一个很关键的地方有区别,就是KK4.4上BOOT_COMPLETED广播由之前的无序发送变为有序发送,即上图中的红色框框圈住的位置,false即为无序发送,ture即为有序发送,首先通过查看android官方原生代码发现不是MTK修改,KK4.4原生即为true,然后查看android的提交历史,找到针对此修改的提交记录如下:







从以上历史记录中可以看到google为了解决calendar的一个问题将BOOT_COMPLETED的广播发送方式由之前的无序改为了有序,同时还进行了延时的发出处理,从而最终影响了整个BOOT_COMPLETED的发送和处理流程,导致BOOT_COMPLETED的处理被安排在无序广播之后,另外由于BOOT_COMPLETED之前还有其他有序广播在待发送队列,只有那些有序广播被顺序处理完才会处理BOOT_COMPLETED,最终在处理有序的BOOT_COMPLETED的时候还会按照优先级进行排序,如果此时静态注册的接收者的优先级大于动态注册的优先级则在发送时还会等待静态注册的接收者的进程起来,然后再继续发送,所以结果就导致系统动态注册的接收者迟迟接收不到BOOT_COMPLETED,影响系统组件的整体功能体验。

六、解决方案

通过以上分析,为了避免在BOOT_COMPLETED广播之前的其他有序广播以及设置了高优先级的静态注册的BOOT_COMPLETED广播的广播接收者占用时间,影响系统组件的接收和功能状态更新,我们给出以下解决方案:
1、将BOOT_COMPLETED广播的发送方式由有序变为无序,这样就是让动态注册的系统组件及时的接收到广播消息,从而能够正常的更新状态,解决因有序而产生的问题。

七、结论

应用解决方案之后系统的行为恢复正常,第三方以及其他应用程序对系统开机的影响被消除,与JB4.2行为基本保持一致,最终达到要求和解决问题的目的。

#analysed by jinshi.song from SWD2 Framework team.
#jinshi.song@jrdcom.com
#201407161600
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: