美文网首页
Android ARN 探究

Android ARN 探究

作者: richy_ | 来源:发表于2018-10-11 16:57 被阅读162次

    参考一下内容总结,有需要请查看原作者内容
    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的信息收集过程

    相关文章

      网友评论

          本文标题:Android ARN 探究

          本文链接:https://www.haomeiwen.com/subject/qhobaftx.html