ANR触发机制分析

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

ANR是一套监控Android应用程序响应是否及时的机制可以把发生ANR比作是引爆炸弹那么整个流程包含三部分组成

  1. 埋定时炸弹system_server进程启动倒计时在规定时间内如果目标应用进程没有干完所有的活则system_server会杀进程。
  2. 拆炸弹在规定时间内干完所有活并及时向system_server报告完成请求解除定时炸弹。
  3. 引爆炸弹system_server立即封装现场抓取快照搜索目标执行慢的罪证(traces)便于后续的案件侦破(调试分析)最后炸毁目标。

常见的anr有servicebroadcastprovider以及input。

Service超时机制

在这里插入图片描述

  1. 客户端(App进程)向system_server进程发起启动服务的请求
  2. biner_1线程接收该请求紧接着向activitymanager线程发送消息埋下定时炸弹
  3. binder_1通知service所在进程准备开始干活
  4. binder_3收到任务后转交给main线程加入messagequeue
  5. 完成service启动的生命周期然后等待sharepreferences持久化
  6. sp执行完成后立刻向system_server汇报工作已完成
  7. system_server的binder_2收到完成工作的汇报后立刻拆除炸弹。如果在炸弹倒计时结束前拆除炸弹则相安无事否则引爆炸弹(触发anr)

Broadcast超时机制

在这里插入图片描述

  1. 客户端(App进程)向system_server进程发起发送广播的请求
  2. biner_1线程接收该请求并转交给activitymanager线程
  3. activitymanager执行任务(processNextBroadcast方法)的过程埋下定时炸弹
  4. activitymanager通知receiver进程(上面图片有误应该是receiver进程)准备干活
  5. binder_3收到任务后转交给main线程加入messagequeue
  6. 完成receiver启动的生命周期发现当前进程还有sp正在执行写入文件的操作便将向system_server汇报的任务交给sp(queue-work-looper线程)
  7. sp完成数据的持久化便可以向system_server汇报工作完成
  8. system_server的binder_2收到完成工作的汇报后立刻拆除炸弹。如果在炸弹倒计时结束前拆除炸弹则相安无事否则引爆炸弹(触发anr)

注意sp从8.0开始采用名叫"queue-work-looper"的handle线程老版本采用newSingleThreadExecutor创建的单线程的线程池。
如果是动态广播或者静态广播没有正在执行持久化操作的sp任务则不需要经过"queue-work-looper"线程中转直接由main线程向system_server汇报工作。
sp的apply将修改的数据项更新到内存然后再异步同步数据到磁盘文件因此很多地方会推荐在主线程调用采用apply方式避免阻塞主线程但静态广播超时检测过程需要sp全部持久化到磁盘如果过度使用apply会增大anr的概率。

Provider超时机制

provider的超时是在provider进程首次启动的时候才会检测在provider进程已启动的场景再次请求provider并不会触发provider超时。
在这里插入图片描述

  1. 客户端App进程向中控系统system_server进程发起获取内容提供者的请求
  2. 中控系统派出一名空闲的通信员binder_1接收该请求检测到内容提供者尚未启动则先通过zygote孵化新进程
  3. 新化的provider进程向中控系统注册自己的存在
  4. 中控系统的通信员2号接收到该信息后向组件管家ActivityManager线程发送消息埋下炸弹
  5. 通信员2号通知工地provider进程的通信员准备开始干活
  6. 通讯员4号binder_4收到任务后转交给包工头main主线程加入包工头的任务队列MessageQueue
  7. 包工头经过一番努力干完活完成provider的安装工作后向中控系统汇报工作已完成
  8. 中控系统的通讯员3号binder_3收到包工头的完工汇报后立刻拆除炸弹。如果在倒计时结束前拆除炸弹则相安无事否则会引发爆炸触发ANR

input超时机制

input的超时检测机制跟service、broadcast、provider截然不同为了更好的理解input过程先来介绍两个重要线程的相关工作

  • InputReader线程负责通过EventHub监听目录/dev/input读取输入事件一旦监听到输入事件则放入到InputDispatcher的mlnBoundQueue队列并通知其处理该事件
  • InputDispatcher线程负责将接收收到的输入事件分发给目标应用窗口分发过程使用到3个事件队列
    1. mlnBoundQueue用于记录InputReader发送过来的输入事件
    2. outBoundQueue用于记录即将分发给目标应用窗口的输入事件
    3. waitQueue用于记录已分发给目标应用且应用尚未处理完成的输入事件
      input的超时机制并非时间到了一定就会爆炸而是处理后续上报事件的过程才会去检测是否该爆炸所以更像是扫雷的过程具体如下图所示。
      在这里插入图片描述
  1. InputReader线程通过EventHub监听底层上报的输入事件一旦收到输入事件则将其放至mlnBoundQueue队列并唤醒InputDispatcher线程
  2. InputDispatcher开始分发输入事件设置埋雷的起点时间。先检测是否有正在处理的事件mPendingEvent如果没有则取出mnBoundQueue队头的事件并将其赋值给mPendingEvent且重置ANR的timeout否则不会从mlnBoundQueue中取出事件也不会重置timeout。然后检查窗口是否就绪
    checkWindowReadyForMorelnputLocked满足以下任一情况则会进入扫雷状态检测前一个正在处理的事件是否超时终止本轮事件分发否则继续执行步骤3。
    • 对于按键类型的输入事件则outboundQueue或者waitQueue不为空
    • 对于非按键的输入事件则waitQueue不为空且等待队头时间超时500ms
  3. 当应用窗口准备就绪则将mPendingevent转移到outBoundQueue队列
  4. 当outBoundQueue不为空且应用管道对端连接状态正常则将数据从outboundQueue中取出事件放入aitQueue队列
  5. InputDispatcher通过socket告知目标应用所在进程可以准备开始干活
  6. App在初始化时默认已创建跟中控系统双向通信的socketpair此时App的包工头main线程收到输入事件后会层层转发到目标窗口来处理
  7. 包工头完成工作后会通过socket向中控系统汇报工作完成则中控系统会将该事件从waitQueue队列中移除。

input超时机制为什么是扫雷而非定时爆炸呢是由于对于input来说即便某次事件执行时间超过timeout时长只要用户后续在没有再生成输入事件则不会触发ANR。这里的扫雷是指当前输入系统中正在处理着某个耗时事件的前提下后续的每一次input事件都会检测前一个正在处理的事件是否超时进入扫雷状态检测当前的时间距离上次输入事件分发时间点是否超过timeout时长。

前台与后台服务

系统对前台服务启动的超时为20s而后台服务超时为200s那么系统是如何区别前台还是后台服务的
在startservice过程根据发起方进程所属的进程调度组来决定被启动的服务是属于前台还是后台。当发起方进程不等于ProcessList.SCHED_GROUP_BACKGROUND(后台进程组)则认为是前台服务否则为后台服务。
进程调度组大体可分为top、前台、后台进程优先级以及进程调度组算法比较复杂其对应关系可理解为adj等于0的进程属于top进程组等于100或200的进程属于前台进程组>200的进程属于后台进程组后台进程组对于用户来说基本是无感知的主要做一些后台工作分配更少的时间片。
在这里插入图片描述
前台服务准确来说是指由处于前台进程调度组的进程发起的服务。这跟常说的fg-service服务有所不同fg-service是指挂有前台通知的服务。

前台与后台广播超时

根据发送广播sendBroadcast(Intent intent中的intent的flags是否包含FLAG_RECEIVER_FOREGROUND来决定把该广播是放入前台广播队列或者后台广播队列前台广播队列的超时为10s后台广播队列的超时为60s默认情况下广播是放入后台广播队列除非指明加上FLAG_RECEIVER_FOREGROUND标识。
后台广播比前台广播拥有更长的超时值同时在广播分发过程遇到后台service的启动mDelayBehindsenvices会延迟分发广播等待service的完成因为等待service而导致的广播ANR会被忽略掉后台广播属于后台进程调度组而前台广播属于前台进程调度组。简而言之后台广播更不容易发生ANR同时执行的速度也会更慢。
另外只有串行处理的广播才有超时机制因为接收者是串行处理的前一个receiver处理慢会影响后一个
receiver并行广播通过一个循环一次性向所有的receiver分发广播事件所以不存在彼此影响的问题则没有广播超时。
前台广播准确来说是指位于前台广播队列的广播

前台与后台ANR

决定是前台还是后台anr取决于该应用发生anr时对用户是否可感知比如拥有当前前台可见的activity的进程或者拥有前台通知的fg-service进程这些是用户可感知的场景后台anr只抓取发生无响应进程的trace也不会收集cpu信息并且会在后台直接杀掉该无响应的进程前台anr准确来说是指对用户可感知的进程发生的anr。

后记

  • 哪些路径会引发anr?
    从埋下定时炸弹到拆炸弹之间的任何一个或多个路径执行慢都会导致anr。
  • 发生anr时从trace来看主线程却处于空闲状态或者停留在非耗时代码的原因有哪些
    可以是抓取trace过于耗时而错过现场可以是主线程消息队列堆积大量消息而最后抓取快照一刻只是瞬时状态可以是广播的"queue-work-looper"一直在处理sp操作。
阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6