Android ARN 探究

参考一下内容总结,有需要请查看原作者内容
google ANRs

Android ANR:原理分析及解决办法

Android应用ANR分析

1 ANR是什么

Application Not Responding,字面意思就是应用无响应,稍加解释就是用户的一些操作无法从应用中获取反馈。

2 产生ANR的原因

Android系统中,ActivityManagerService(简称AMS)WindowManagerService(简称WMS)会检测App的响应时间,如果App在特定时间无法响应屏幕触摸或键盘输入事件,或者特定事件没有处理完毕,就会出现ANR。

以下四个条件都可以造成ANR发生:

  • InputDispatching Timeout5秒内无法响应屏幕触摸事件或键盘输入事件
  • BroadcastQueue Timeout :在执行前台广播(BroadcastReceiver)的onReceive()函数时10秒没有处理完成,后台为60秒
  • Service Timeout :前台服务20秒内,后台服务在200秒内没有执行完毕。
  • ContentProvider Timeout :ContentProvider的publish在10s内没进行完。

一下几点可能造成事件处理不及时:

  1. 应用自身引起,例如:
  • 主线程阻塞、IOWait等;
  1. 其他进程间接引起,例如:
  • 当前应用进程进行进程间通信请求其他进程,其他进程的操作长时间没有反馈;
  • 其他进程的CPU占用率高,使得当前应用进程无法抢占到CPU时间片;

3 常见ANR处理

  • 主线程阻塞或主线程数据读取

解决办法:避免死锁的出现,使用子线程来处理耗时操作或阻塞任务。尽量避免在主线程query provider、请不要滥用SharedPreference

  • CPU满负荷,I/O阻塞

解决办法:文件读写或数据库操作放在子线程异步操作。

  • 内存不足

解决办法:AndroidManifest.xml文件<applicatiion>中可以设置 android:largeHeap="true",以此增大App使用内存。不过不建议使用此法,从根本上防止内存泄漏,优化内存使用才是正道。

  • 各大组件ANR

各大组件生命周期中也应避免耗时操作,注意BroadcastReciever的onRecieve()、后台Service和ContentProvider也不要执行太长时间的任务。

4 ANR日志分析

当发生ANR的时候main.log中会出现提示;

09-20 16:11:03.488 I/art     (30941): Thread[3,tid=30947,WaitingInMainSignalCatcherLoop,Thread*=0xa14d2d00,peer=0x22c050d0,"Signal Catcher"]: reacting to signal 3 --ANR发生的时间和线程
09-20 16:11:05.953 I/art     (30941): Wrote stack traces to '/data/anr/traces.txt'

ANR的Log信息保存在:/data/anr/traces.txt,有些时候也可以在system.log中找到。

09-20 16:11:20.595 E/ActivityManager( 1983): ANR in com.android.settings (com.android.settings/.SubSettings)
09-20 16:11:20.595 E/ActivityManager( 1983): PID: 30941
09-20 16:11:20.595 E/ActivityManager( 1983): Reason: Input dispatching timed out (Waiting because no window has focus but there is a focused application that may eventually add a window when it finishes starting up.) -----ANR的类型
09-20 16:11:20.595 E/ActivityManager( 1983): Load: 14.76 / 11.52 / 9.87 --CPU的负载情况
09-20 16:11:20.595 E/ActivityManager( 1983): CPU usage from 0ms to 17468ms later (2018-09-20 16:11:03.004 to 2018-09-20 16:11:20.472) with 99% awake: --发生ANR后一段时间的使用情况,有时候这个会显示 ago ,发生ANR之前的CPU使用情况
09-20 16:11:20.595 E/ActivityManager( 1983):   35% 1983/system_server: 23% user + 11% kernel / faults: 16914 minor 517 major
09-20 16:11:20.595 E/ActivityManager( 1983):   20% 30941/com.android.settings: 13% user + 7% kernel / faults: 17933 minor 478 major
09-20 16:11:20.595 E/ActivityManager( 1983):   14% 28922/com.huaqin.speech.recognizer: 10% user + 3.5% kernel / faults: 13499 minor 104 major
09-20 16:11:20.595 E/ActivityManager( 1983):   14% 100/kswapd0: 0% user + 14% kernel
09-20 16:11:20.595 E/ActivityManager( 1983):   8% 383/mediaserver: 4.8% user + 3.2% kernel / faults: 656 minor 3 major
09-20 16:11:20.595 E/ActivityManager( 1983):   7.2% 381/media.extractor: 7.1% user + 0.1% kernel / faults: 292 minor 2 major
...

4.1 log信息说明

1) ANR in xxx,PID: xxx,Reason:xxx

  1. 导致ANR的包名(com.android.settings),类名(com.android.settings/.SubSettings),进程PID(30941)
  2. 导致ANR的原因:Input dispatching timed out
  3. 系统中活跃进程的CPU占用率,关键的一句:100%TOTAL: 4.8% user + 7.6% kernel + 87% iowait;表示CPU占用满负荷了,其中绝大数是被iowait即I/O操作占用了。我们就可以大致得出是io操作导致的ANR。

虽然给出了ANR的原因,但是这种原因并不能帮我们直接发现问题根源

一般来说原因有两类,分别对应于InputDispatcher.cpp文件的两段代码,InputDispatcher是派发事件过程中的一个中转类,每次派发事件时他会进行如下判断

a) 判断是否有focused组件以及focusedApplication

这种一般是在应用启动时触发,比如启动时间过长在这过程中触发了 keyevent 或者 trackball moteionevent 就会出现

image.png

这种情况下,对应的Reason类似于这样

Reason: Input dispatching timed out (Waiting because no window has focus but there is a focused application that may eventually add a window when it finishes starting up.)

我们Monkey脚本中出现的ANR问题绝大部分都是属于这种问题,实际上出现这种ANR之前我们应用一般发生了崩溃需要重启,但是重启进行的操作比较耗时,但是具体并不清楚耗时的地方。

b) 判断前面的事件是否及时完成

这里的事件包含keyevent和touchevent,虽然它们对允许的延时要求不一样,但最终都会执行到如下代码

image.png

这种情况下,对应的Reason类似于这样

Reason: Input dispatching timed out (Waiting to send non-key event because the touched window has not finished processing certain input events that were delivered to it over 500.0ms ago. Wait queue length: 10. Wait queue head age: 5591.3ms.)

出现这种问题意味着主线程正在执行其他的事件但是比较耗时导致输入事件无法及时处理。

2) Load: 14.76 / 11.52 / 9.87 CPU的负载情况

CPU负载是指某一时刻系统中运行队列长度之和加上当前正在CPU上运行的进程数,而CPU平均负载可以理解为一段时间内正在使用和等待使用CPU的活动进程的平均数量。在Linux中“活动进程”是指当前状态为运行或不可中断阻塞的进程。通常所说的负载其实就是指平均负载。

用一个从网上看到的很生动的例子来说明(不考虑CPU时间片的限制),把设备中的一个单核CPU比作一个电话亭,把进程比作正在使用和等待使用电话的人,假如有一个人正在打电话,有三个人在排队等待,此刻电话亭的负载就是4。使用中会不断的有人打完电话离开,也会不断的有其他人排队等待,为了得到一个有参考价值的负载值,可以规定每隔5秒记录一下电话亭的负载,并将某一时刻之前的一分钟、五分钟、十五分钟的的负载情况分别求平均值,最终就得到了三个时段的平均负载。

实际上我们通常关心的就是在某一时刻的前一分钟、五分钟、十五分钟的CPU平均负载,例如以上日志中这三个值分别是14.76 / 11.52 / 9.87,说明前一分钟内正在使用和等待使用CPU的活动进程平均有14.76个,依此类推。在大型服务器端应用中主要关注的是第五分钟和第十五分钟的两个值,但是Android主要应用在便携手持设备中,有特殊的软硬件环境和应用场景,短时间内的系统的较高负载就有可能造成ANR,所以我认为一分钟内的平均负载相对来说更具有参考价值

CPU的负载和使用率没有必然关系,有可能只有一个进程在使用CPU,但执行的是复杂的操作;也有可能等待和正在使用CPU的进程很多,但每个进程执行的都是简单操作。
实际处理问题时偶尔会遇到由于平均负载高引起的ANR,典型的特征就是系统中应用进程数量多,CPU总使用率较高,但是每个进程的CPU使用率不高,当前应用进程主线程没有异常阻塞,一分钟内的CPU平均负载较高。

3) CPU usage xxx

CPU usage from 75634ms to 0ms ago:
25% 869/system_server: 19% user + 6.1% kernel / faults: 86246 minor
...
CPU usage from 601ms to 1132ms later with 99% awake
...

这里会分两段打印出出现ANR前后CPU的使用率情况,每一段包含了所有进程的CPU使用率,如果说某个进程在ANR发生时CPU使用率出现很高的情况,那么就可以知道这个进程在做非常消耗CPU的事情,一般这种情况下这个进程就是ANR进程,而消耗CPU的这个事情往往就是导致ANR的根源。

4.2 trace信息分析

1. 进程资源状态信息

Build fingerprint: 'Xiaomi/virgo/virgo:6.0.1/MMB29M/6.3.21:user/release-keys'
ABI: 'arm'
Build type: optimized
Zygote loaded classes=4124 post zygote classes=18
Intern table: 51434 strong; 17 weak
JNI: CheckJNI is off; globals=286 (plus 277 weak)
Libraries: /system/lib/libandroid.so /system/lib/libcompiler_rt.so /system/lib/libjavacrypto.so /system/lib/libjnigraphics.so /system/lib/libmedia_jni.so /system/lib/libmiuinative.so /system/lib/libsechook.so /system/lib/libwebviewchromium_loader.so libjavacore.so (9)
Heap: 50% free, 16MB/33MB; 33690 objects
Dumping cumulative Gc timings
Total number of allocations 33690
Total bytes allocated 16MB
Total bytes freed 0B
Free memory 16MB
Free memory until GC 16MB
Free memory until OOME 111MB
Total memory 33MB
Max memory 128MB
Zygote space size 1624KB
Total mutator paused time: 0
Total time waiting for GC to complete: 0
Total GC count: 0
Total GC time: 0
Total blocking GC count: 0
Total blocking GC time: 0

这里打印了一大段关于硬件状态的信息。

2. 每条线程的信息

"main" prio=5 tid=1 Native // 输出了线程名,优先级,线程号,线程状态,带有『deamon』字样的线程表示守护线程,即DDMS中『*』线程
  | group="main" sCount=1 dsCount=0 obj=0x7541b3c0 self=0xb4cf6500 // 输出了线程组名,sCount被挂起次数,dsCount被调试器挂起次数,obj表示线程对象的地址,self表示线程本身的地址
  | sysTid=4280 nice=-1 cgrp=default sched=0/0 handle=0xb6f5cb34 // sysTid是Linux下的内核线程id,nice是线程的调度优先级,cgrp是调度属组,sched分别标志了线程的调度策略和优先级,handle是线程的处理函数地址。
  | state=S schedstat=( 52155108 81807757 159 ) utm=2 stm=3 core=0 HZ=100 // state是调度状态;schedstat从 /proc/[pid]/task/[tid]/schedstat读出,三个值分别表示线程在cpu上执行的时间、线程的等待时间和线程执行的时间片长度,有的android内核版本不支持这项信息,得到的三个值都是0;utm是线程用户态下使用的时间值(单位是jiffies);stm是内核态下的调度时间值;core是最后执行这个线程的cpu核的序号。
  | stack=0xbe121000-0xbe123000 stackSize=8MB
  | held mutexes=
//调用栈信息
  native: #00 pc 00040984  /system/lib/libc.so (__epoll_pwait+20)
  native: #01 pc 00019f5b  /system/lib/libc.so (epoll_pwait+26)
  native: #02 pc 00019f69  /system/lib/libc.so (epoll_wait+6)
  native: #03 pc 00012c57  /system/lib/libutils.so (android::Looper::pollInner(int)+102)
  native: #04 pc 00012ed3  /system/lib/libutils.so (android::Looper::pollOnce(int, int*, int*, void**)+130)
  native: #05 pc 00082bed  /system/lib/libandroid_runtime.so (android::NativeMessageQueue::pollOnce(_JNIEnv*, _jobject*, int)+22)
  native: #06 pc 0000055d  /data/dalvik-cache/arm/system@framework@boot.oat (Java_android_os_MessageQueue_nativePollOnce__JI+96)
  at android.os.MessageQueue.nativePollOnce(Native method)
  at android.os.MessageQueue.next(MessageQueue.java:323)
  at android.os.Looper.loop(Looper.java:135)
  at android.app.ActivityThread.main(ActivityThread.java:5435)
  at java.lang.reflect.Method.invoke!(Native method)
  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:735)
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:625)

该有的描述上面已经用注释的方式放入了,最后的部分是调用栈信息。

----- pid 12838 at 2016-05-30 10:41:04 -----
Cmd line: 略
// 进程状态信息省略

suspend all histogram:  Sum: 1.456ms 99% C.I. 3us-508.799us Avg: 97.066us Max: 523us
DALVIK THREADS (19):
"Signal Catcher" daemon prio=5 tid=2 Runnable
  | group="system" sCount=0 dsCount=0 obj=0x32c02100 self=0xb82f1d40
  | sysTid=12843 nice=0 cgrp=bg_non_interactive sched=0/0 handle=0xb39ec930
  | state=R schedstat=( 10914800 1156480 11 ) utm=0 stm=0 core=2 HZ=100
  | stack=0xb38f0000-0xb38f2000 stackSize=1014KB
  | held mutexes= "mutator lock"(shared held)
  native: #00 pc 00371069  /system/lib/libart.so (_ZN3art15DumpNativeStackERNSt3__113basic_ostreamIcNS0_11char_traitsIcEEEEiPKcPNS_9ArtMethodEPv+160)
  native: #01 pc 003508c3  /system/lib/libart.so (_ZNK3art6Thread4DumpERNSt3__113basic_ostreamIcNS1_11char_traitsIcEEEE+150)
  native: #02 pc 0035a5bb  /system/lib/libart.so (_ZN3art14DumpCheckpoint3RunEPNS_6ThreadE+442)
  native: #03 pc 0035b179  /system/lib/libart.so (_ZN3art10ThreadList13RunCheckpointEPNS_7ClosureE+212)
  native: #04 pc 0035b6a7  /system/lib/libart.so (_ZN3art10ThreadList4DumpERNSt3__113basic_ostreamIcNS1_11char_traitsIcEEEE+142)
  native: #05 pc 0035bdb7  /system/lib/libart.so (_ZN3art10ThreadList14DumpForSigQuitERNSt3__113basic_ostreamIcNS1_11char_traitsIcEEEE+334)
  native: #06 pc 00331179  /system/lib/libart.so (_ZN3art7Runtime14DumpForSigQuitERNSt3__113basic_ostreamIcNS1_11char_traitsIcEEEE+72)
  native: #07 pc 0033b27d  /system/lib/libart.so (_ZN3art13SignalCatcher13HandleSigQuitEv+928)
  native: #08 pc 0033bb61  /system/lib/libart.so (_ZN3art13SignalCatcher3RunEPv+340)
  native: #09 pc 00041737  /system/lib/libc.so (_ZL15__pthread_startPv+30)
  native: #10 pc 00019433  /system/lib/libc.so (__start_thread+6)
  (no managed stack frames)
"main" prio=5 tid=1 Blocked //主线程Block
  | group="main" sCount=1 dsCount=0 obj=0x759002c0 self=0xb737fee8
  | sysTid=12838 nice=-1 cgrp=bg_non_interactive sched=0/0 handle=0xb6f1eb38
  | state=S schedstat=( 743081924 64813008 709 ) utm=50 stm=23 core=4 HZ=100
  | stack=0xbe54e000-0xbe550000 stackSize=8MB
  | held mutexes=
  kernel: (couldn't read /proc/self/task/12838/stack)
  native: #00 pc 00016aa4  /system/lib/libc.so (syscall+28)
  native: #01 pc 000f739d  /system/lib/libart.so (_ZN3art17ConditionVariable4WaitEPNS_6ThreadE+96)
  native: #02 pc 002bcd8d  /system/lib/libart.so (_ZN3art7Monitor4LockEPNS_6ThreadE+408)
  native: #03 pc 002bed73  /system/lib/libart.so (_ZN3art7Monitor4WaitEPNS_6ThreadExibNS_11ThreadStateE+922)
  native: #04 pc 002bfbaf  /system/lib/libart.so (_ZN3art7Monitor4WaitEPNS_6ThreadEPNS_6mirror6ObjectExibNS_11ThreadStateE+142)
  native: #05 pc 002d1403  /system/lib/libart.so (_ZN3artL11Object_waitEP7_JNIEnvP8_jobject+38)
  native: #06 pc 0000036f  /data/dalvik-cache/arm/system@framework@boot.oat (Java_java_lang_Object_wait__+74)
  at java.lang.Object.wait!(Native method)
  - waiting to lock <0x0520de84> (a java.lang.Object) held by thread 22 //等待22线程的锁对象
  at com.xx(unavailable:-1)
  - locked <0x00e3266d> 
  - locked <0x0520de84> (a java.lang.Object)
  at com.xx.R(unavailable:-1)
  at com.xx.ux(unavailable:-1)
  // 其余栈略
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:684)
// 其他线程省略
"Thread-654" prio=5 tid=22 Blocked //tid=22
  | group="main" sCount=1 dsCount=0 obj=0x32c027c0 self=0xb83e9750
  | sysTid=12891 nice=0 cgrp=bg_non_interactive sched=0/0 handle=0x9cf1c930
  | state=S schedstat=( 50601200 1215760 62 ) utm=4 stm=0 core=7 HZ=100
  | stack=0x9ce1a000-0x9ce1c000 stackSize=1038KB
  | held mutexes=
  at com.yy(unavailable:-1)
  - waiting to lock <0x00e3266d> held by thread 1 //等待tid 1 持有的的锁对象,发生了死锁
  at com.yy.MX(unavailable:-1)
  at com.yy.run(unavailable:-1)
  - locked <0x0520de84> (a java.lang.Object) //锁住了这个对象
  at java.lang.Thread.run(Thread.java:833)

从traces文件种可以很明显的看到我们的主线程处于Blcoked状态,详细查看Blcoked的原因知道,它在等待一个被22号线程持有的对象锁,于是我们查看tid=22的线程,可以看出这个线程的确锁住了一个对象,该对象正是主线程正在等待上锁的对象,那这个线程为何没有释放锁呢,因为它在等一个被1号线程持有的对象锁,因此死锁问题导致了ANR现象。

4.3 !!ANR SOP

ANR_SOP

5 ANR源码分析

特别声明:文章 理解Android ANR的触发原理 分别记录了由ServiceBroadcastReceiverContentProvider造成的ANR。下文引用该文代码,并依据自己的简单理解作总结。

5.1Service造成的Service Timeout

Service Timeout是位于"ActivityManager"线程中的AMS.MainHandler收到SERVICE_TIMEOUT_MSG消息时触发。

5.1.1 发送延时消息

Service进程attach到system_server进程的过程中会调用realStartServiceLocked,紧接着mAm.mHandler.sendMessageAtTime()来发送一个延时消息,延时的时常是定义好的,如前台Service的20秒。ActivityManager线程中的AMS.MainHandler收到SERVICE_TIMEOUT_MSG消息时会触发。

AS.realStartServiceLocked

//com.android.server.am.ActiveServices
private final void realStartServiceLocked(ServiceRecord r,
        ProcessRecord app, boolean execInFg) throws RemoteException {
    ...
    //发送delay消息(SERVICE_TIMEOUT_MSG)
    bumpServiceExecutingLocked(r, execInFg, "create");
    try {
        ...
        //最终执行服务的onCreate()方法
        app.thread.scheduleCreateService(r, r.serviceInfo,
                mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
                app.repProcState);
    } catch (DeadObjectException e) {
        mAm.appDiedLocked(app);
        throw e;
    } finally {
        ...
    }
}

AS.bumpServiceExecutingLocked

//com.android.server.am.ActiveServices#bumpServiceExecutingLocked
private final void bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why) {
    ... 
    scheduleServiceTimeoutLocked(r.app);
}

void scheduleServiceTimeoutLocked(ProcessRecord proc) {
    if (proc.executingServices.size() == 0 || proc.thread == null) {
        return;
    }
    long now = SystemClock.uptimeMillis();
    Message msg = mAm.mHandler.obtainMessage(
            ActivityManagerService.SERVICE_TIMEOUT_MSG);
    msg.obj = proc;
    
    //当超时后仍没有remove该SERVICE_TIMEOUT_MSG消息,则执行service Timeout流程
    mAm.mHandler.sendMessageAtTime(msg,
        proc.execServicesFg ? (now+SERVICE_TIMEOUT) : (now+ SERVICE_BACKGROUND_TIMEOUT));
}

5.1.2 进入目标进程的主线程创建Service

经过Binder等层层调用进入目标进程的主线程 handleCreateService(CreateServiceData data)。

//android.app.ActivityThread#handleCreateService   
private void handleCreateService(CreateServiceData data) {
        ...
        java.lang.ClassLoader cl = packageInfo.getClassLoader();
        Service service = (Service) cl.loadClass(data.info.name).newInstance();
        ...
        try {
            //创建ContextImpl对象
            ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
            context.setOuterContext(service);
            //创建Application对象
            Application app = packageInfo.makeApplication(false, mInstrumentation);
            service.attach(context, this, data.info.name, data.token, app,
                    ActivityManagerNative.getDefault());
            //调用服务onCreate()方法 
            service.onCreate();
            
            //取消AMS.MainHandler的延时消息
            ActivityManagerNative.getDefault().serviceDoneExecuting(
                    data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
        } catch (Exception e) {
            ...
        }
    }

这个方法中会创建目标服务对象,以及回调常用的ServiceonCreate()方法,紧接着通过serviceDoneExecuting()回到system_server执行取消AMS.MainHandler的延时消息

5.1.3 回到system_server执行取消AMS.MainHandler的延时消息

//com.android.server.am.ActiveServices#serviceDoneExecutingLocked
private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying,
            boolean finishing) {
    ...
    if (r.executeNesting <= 0) {
        if (r.app != null) {
            r.app.execServicesFg = false;
            r.app.executingServices.remove(r);
            if (r.app.executingServices.size() == 0) {
                //当前服务所在进程中没有正在执行的service
                mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
        ...
    }
    ...
}

此方法中Service逻辑处理完成则移除之前延时的消息SERVICE_TIMEOUT_MSG。如果没有执行完毕不调用这个方法,则超时后会发出SERVICE_TIMEOUT_MSG来告知ANR发生。

5.2 BroadcastReceiver造成的BroadcastQueue Timeout

BroadcastReceiver Timeout是位于"ActivityManager"线程中的BroadcastQueue.BroadcastHandler收到BROADCAST_TIMEOUT_MSG消息时触发。

5.2.1 处理广播函数 processNextBroadcast() 中 broadcastTimeoutLocked(false) 发送延时消息

广播处理顺序为先处理并行广播,再处理当前有序广播。

//com.android.server.am.BroadcastQueue#processNextBroadcast
final void processNextBroadcast(boolean fromMsg) {
    synchronized(mService) {
        ...
        // 处理当前有序广播
        do {
            r = mOrderedBroadcasts.get(0);
            //获取所有该广播所有的接收者
            int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
            if (mService.mProcessesReady && r.dispatchTime > 0) {
                long now = SystemClock.uptimeMillis();
                if ((numReceivers > 0) &&
                        (now > r.dispatchTime + (2*mTimeoutPeriod*numReceivers))) {
                    //step 1. 发送延时消息,这个函数处理了很多事情,比如广播处理超时**结束**广播,下面附了代码
                    broadcastTimeoutLocked(false);
                    ...
                }
            }
            if (r.receivers == null || r.nextReceiver >= numReceivers
                    || r.resultAbort || forceReceive) {
                if (r.resultTo != null) {
                    //2. 处理广播消息消息
                    performReceiveLocked(r.callerApp, r.resultTo,
                        new Intent(r.intent), r.resultCode,
                        r.resultData, r.resultExtras, false, false, r.userId);
                    r.resultTo = null;
                }
                //3. 取消广播超时ANR消息
                cancelBroadcastTimeoutLocked();
            }
        } while (r == null);
        ...

        // 获取下条有序广播
        r.receiverTime = SystemClock.uptimeMillis();
        if (!mPendingBroadcastTimeoutMessage) {
            long timeoutTime = r.receiverTime + mTimeoutPeriod;
            ////step 2 设置广播超时
            setBroadcastTimeoutLocked(timeoutTime);
        }
        ...
    }
}

上文的step 1. broadcastTimeoutLocked(false)函数:记录时间信息并调用函数设置发送延时消息

//com.android.server.am.BroadcastQueue#broadcastTimeoutLocked
final void broadcastTimeoutLocked(boolean fromMsg) {
    ...
        long now = SystemClock.uptimeMillis();
        if (fromMsg) {
            if (mService.mDidDexOpt) {
                // Delay timeouts until dexopt finishes.
                mService.mDidDexOpt = false;
                long timeoutTime = SystemClock.uptimeMillis() + mTimeoutPeriod;
                //step 2
                setBroadcastTimeoutLocked(timeoutTime);
                return;
            }
            if (!mService.mProcessesReady) {
                return;
            }

            long timeoutTime = r.receiverTime + mTimeoutPeriod;
            if (timeoutTime > now) {
                // step 2
                setBroadcastTimeoutLocked(timeoutTime);
                return;
            }
        }

上文的step 2.setBroadcastTimeoutLocked函数: 设置广播超时具体操作,同样是发送延时消息

//参数timeoutTime是当前时间加上设定好的超时时间
//long timeoutTime = SystemClock.uptimeMillis() + mTimeoutPeriod;
final void setBroadcastTimeoutLocked(long timeoutTime) {
    if (! mPendingBroadcastTimeoutMessage) {
        Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this);
        mHandler.sendMessageAtTime(msg, timeoutTime);
        mPendingBroadcastTimeoutMessage = true;
    }
}

mTimeoutPeriod 也就是前台队列的10s和后台队列的60s。

public ActivityManagerService(Context systemContext) {
    ...
    static final int BROADCAST_FG_TIMEOUT = 10 * 1000;
    static final int BROADCAST_BG_TIMEOUT = 60 * 1000;
    ...
    mFgBroadcastQueue = new BroadcastQueue(this, mHandler,
            "foreground", BROADCAST_FG_TIMEOUT, false);
    mBgBroadcastQueue = new BroadcastQueue(this, mHandler,
            "background", BROADCAST_BG_TIMEOUT, true);
    ...
}

5.2.2 在processNextBroadcast()过程,执行完performReceiveLocked后调用cancelBroadcastTimeoutLocked

cancelBroadcastTimeoutLocked :处理广播消息函数 processNextBroadcast() 中 performReceiveLocked() 处理广播消息完毕则调用 cancelBroadcastTimeoutLocked() 取消超时消息。

//com.android.server.am.BroadcastQueue#cancelBroadcastTimeoutLocked
final void cancelBroadcastTimeoutLocked() {
    if (mPendingBroadcastTimeoutMessage) {
        mHandler.removeMessages(BROADCAST_TIMEOUT_MSG, this);
        mPendingBroadcastTimeoutMessage = false;
    }
}

5.3 ContentProvider的ContentProvider Timeout

ContentProvider Timeout是位于”ActivityManager”线程中的AMS.MainHandler收到CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG消息时触发。
参考理解Android ANR的触发原理第四节

5.4 Android ANR的信息收集

无论是四大组件或者进程等只要发生ANR,最终都会调用AMS.appNotResponding()方法。
参考:理解Android ANR的信息收集过程

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容

  • 最近在准备android面试,整理了下相关的面试题,分为如下三个部分:android部分、Java部分、算法面试题...
    JasmineBen阅读 7,057评论 10 137
  • 关于ANR,以前只知道Activity、BroadCastReceiver、Service三种组件的ANR时限、一...
    蓝灰_q阅读 11,265评论 1 15
  • 一、ANR说明和原因 1.1 简介 ANR全称:Application Not Responding,也就是应用程...
    Marker_Sky阅读 98,492评论 6 117
  • 2.1 Activity 2.1.1 Activity的生命周期全面分析 典型情况下的生命周期:在用户参与的情况下...
    AndroidMaster阅读 3,017评论 0 8
  • 娉娉,坐标广州 目前餐饮集团高管,害怕一张办公桌陪自己一辈子,不甘于平淡。带着初心,希望拥抱更多的美好。
    tyy093阅读 208评论 0 0