Android中ANR分析以及出现场景

ANR分析以及出现场景

ANR全称Application Not Response, 应用程序无响应。当应用5s内对用户的输入或点击事件无响应时,Android便认为应用无响应,弹出ANR Dialog. Android应用在启动时会创建一个UI线程也就是我们常说的主线程,该线程只负责UI的渲染,不能做耗时操作,否则就会被阻塞,可能引起ANR。

1、触发ANR的条件

  • Activity内Input事件超时(keyDispatchingTimedOut): 对事件5s无法完成处理

  • BroadcastReceiver超时(BroadcastQueue Timeout):对事件10s无法完成处理,后台60s

  • Service超时(Service Timeout):各个生命周期内在特定时间20s无法完成处理,后台 Service 200s, 前台Service 5s

  • ContentProvider超时(ContentProvider Timeout): ContentProvider的publish在10s内没进行完

2、典型ANR的场景

  • 1、主线程进行了耗时IO操作、耗时计算、网络请求

  • 2、主线程错误操作,比如Thread.wait()、Thread.sleep()、Thread.join()等

  • 3、主线程频繁进行IO操作,比如读写文件或者数据库

  • 4、多线程操作的死锁,导致主线程等待超时

  • 5、多进程下通信,当前进程与其他进程通信导致操作时间变长没有响应

  • 6、四大组件的ANR

  • 7、CPU占用过高,无法及时获取时间片来处理,导致超时。

3、ANR的定位分析

分析方法一: Logcat

当发生ANR的时候,可以看到Logcat会记录ANR发生的时间,以及线程的id以及出现的原因。例如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

分析方法二: traces.txt(最常用的方法)

当发生ANR的时候,Logcat中会提示Wrote stack traces to '/data/anr/traces.txt' ANR的日志保存在/data/anr/traces.txt, 每一次新的ANR出现会把之前ANR信息给覆盖掉。使用adb pull /data/anr/traces.txt命令,导出anr日志文件。但是需要注意的是在高版本设备上会出现权限拒绝的问题,所以需要adb bugreport,然后就会得到一个zip包,解压出现就可以看到FS/data/anr目录下都是日志文件。

04-01 13:12:11.572 I/InputDispatcher( 220): Application is not responding:Window{2b263310com.android.email/com.android.email.activity.SplitScreenActivitypaused=false}.
5009.8ms since event, 5009.5ms since waitstarted
04-0113:12:11.572 I/WindowManager( 220): Input event 
dispatching timedout sending 
tocom.android.email/com.android.email.activity.SplitScreenActivity

04-01 13:12:14.123 I/Process( 220): Sending signal. PID: 21404 SIG:3---发生ANR的时间和生成trace.txt的时间
04-01 13:12:14.123 I/dalvikvm(21404):threadid=4: reacting to signal 3 ……
04-0113:12:15.872 E/ActivityManager( 220): ANR in com.android.email(com.android.email/.activity.SplitScreenActivity)
04-0113:12:15.872 E/ActivityManager( 220): Reason:keyDispatchingTimedOut  -----ANR的类型
04-0113:12:15.872 E/ActivityManager( 220): Load: 8.68 / 8.37 / 8.53 --CPU的负载情况
04-0113:12:15.872 E/ActivityManager( 220): CPUusage from 4361ms to 699ms ago ----CPU在ANR发生前的使用情况;备注:这个ago,是发生前一段时间的使用情况,不是当前时间点的使用情况;

04-0113:12:15.872 E/ActivityManager( 220): 5.5%21404/com.android.email: 1.3% user + 4.1% kernel / faults:
10 minor
...
04-0113:12:15.872 E/ActivityManager( 220): 100%TOTAL: 4.8% user + 7.6% kernel + 87% iowait----注意这行:注意87%的iowait
04-0113:12:15.872 E/ActivityManager( 220): CPUusage from 3697ms to 4223ms later:-- ANR后CPU的使用量

从Logcat中可以得到以下信息:

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

分析方法三: DDMS

使用DDMS的Update Threads工具可以分为如下几步

  • 选择需要查看的进程
  • 点击Update Threads按钮
  • 在Threads视图查看该进程的所有线程状态

选择需要查看的进程并点击更新线程按钮

阅读Update Threads的输出

Update Threads工具可以输出当前进程的所有线程的状态,上半部分是线程列表,选中其中一条下半部分将展现出该线程当前的调用栈。可以选择主线程查看它的调用栈。

分析方法四: 如果没有DDMS可以使用AS中的Analyze Stack Trace 分析ANR问题

可以把/data/anr/traces.txt中的内容,粘贴到AS中的Analyze Stack Trace中,可以分析每个线程的状态是否出现死锁以及线程阻塞导致出现ANR。

1) 文件内容解析

在traces文件中我们会看到很多段类似于如下文本的内容,其中每一段是一个进程,N段表示N个进程,共同描述了出现ANR时系统进程的状况

----- pid 8962 at 2019-12-01 14:03:40 -----
Cmd line: com.mikyou.androiddevelop
Build fingerprint: 'OnePlus/OnePlus7Pro_CH/OnePlus7Pro:10/QKQ1.190716.003/1911010700:user/release-keys'
ABI: 'arm64'
Build type: optimized
Zygote loaded classes=9247 post zygote classes=373
Dumping registered class loaders
  • 进程的头部信息

    ----- pid 8962 at 2019-12-01 14:03:40 -----
    Cmd line: com.mikyou.androiddevelop //进程名
  • 主线程状态信息

    main表示主线程,prio=5线程优先级,tid是线程id,然后Sleeping表示对于线程状态的描述,查看主线程的状态。可以发现主线程处于Sleeping状态。这里对线程的描述多种多样,简单解释下上面出现的几种状态

    • waiting on condition(等待某个事件出现)
    • waiting for monitor entry(等待进入临界区)
    • runnable(正在运行)
    • in Object.wait(处于等待状态)
    • sleeping 睡眠状态

4、ANR检测

1.使用Android SDK自带的StrictMode(严格模式)进行检测
StrictMode使用教程地址:
https://droidyue.com/blog/2015/09/26/android-tuning-tool-strictmode/
2.使用BlockCanary
BlockCanary是一个非侵入时的性能监控函数库,其特点有:
a.非侵入式,简单的两行就打开监控,不需要到处打点,破坏代码优雅性。
b.精准,输出的信息可以帮助定位到问题所在(精确到行),不需要像Logcat一样,慢慢去找。
目前包括了核心监控输出文件,以及UI显示卡顿信息功能且集成简单快速

5、ANR机制原理和源码分析

ANR是一套监控Android应用响应是否及时的机制,可以大致分为三个部分:

  • 1、中控系统system_server进程启动倒计时,在规定时间内如果没有执行完所有的任务,就会触发ANR

  • 2、在规定时间内执行完所有任务后,及时向system_sever报告完成,请求解除ANR预警,从而正常避免了ANR。

  • 3、触发ANR的过程,产生ANR异常的snapshot,存入本地anr/traces.txt中,以便于排查分析问题,最后就是触发ANR

service超时机制

startService后,在ActiveServices中realStartServiceLocked方法中在执行service的onCreate方法之前先调用了bumpServiceExecutingLocked方法,来发送delay消息。

private final void realStartServiceLocked(ServiceRecord r, ProcessRecord app, boolean execInFg) throws RemoteException {
    ...
    //发送delay消息(SERVICE_TIMEOUT_MSG),【见小节2.1.2】
    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 {
        ...
    }
}

然后,调用ActiveService中的 bumpServiceExecutingLocked,然后调用scheduleServiceTimeoutLocked方法触发service超时。

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;
        }
        Message msg = mAm.mHandler.obtainMessage(
                ActivityManagerService.SERVICE_TIMEOUT_MSG);
        msg.obj = proc;
        //当超时后仍没有remove该SERVICE_TIMEOUT_MSG消息,则执行service Timeout流程
        mAm.mHandler.sendMessageDelayed(msg,
                proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);//通过sendMessageDelayed方法向主线程发送超时消息,delay时间就是设置的前台或后台Service的超时时间
    }

    // 普通service超时时间20s
    static final int SERVICE_TIMEOUT = 20*1000;

    // 后台service超时时间200s
    static final int SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10;

    // 前台service超时时间5s
    // How long the startForegroundService() grace period is to get around to
    // calling startForeground() before we ANR + stop it.
    static final int SERVICE_START_FOREGROUND_TIMEOUT = 5*1000;

上述步骤主要是在Service启动时,就埋下超时定时发送的Message. 也就是所谓埋炸弹的过程,然后在system_server进程realStartServiceLocked()调用的过程会埋下一颗炸弹, 超时没有启动完成则会爆炸. 那么什么时候会拆除这颗炸弹的引线呢? 经过Binder等层层调用进入目标进程的主线程handleCreateService()的过程.

private void handleCreateService(CreateServiceData data) {
        ...
        Service service = null;
        try {
            java.lang.ClassLoader cl = packageInfo.getClassLoader();
            service = (Service) cl.loadClass(data.info.name).newInstance();
        } catch (Exception e) {
            ...
        }

        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,
                    ActivityManager.getService());
            //调用服务onCreate()方法        
            service.onCreate();
            mServices.put(data.token, service);
            try {
                //拆除炸弹引线, 告知service执行完毕,调用serviceDoneExecuting
                ActivityManager.getService().serviceDoneExecuting(
                        data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        } catch (Exception e) {
           ...
        }
    }

当service执行onCreate之后,就会执行serviceDoneExecuting,进行拆除炸弹的操作,也实际上就是把原来刚进入启动service时候,定时超时消息从消息队列中remove掉即可。

 private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying,
            boolean finishing) {
        ...
        if (r.executeNesting <= 0) {
            if (r.app != null) {
                //如果当前服务所在进程中没有正在执行的service,就移除SERVICE_TIMEOUT_MSG的Message.所以service的onCreate中不能进行耗时的操作。
                if (r.app.executingServices.size() == 0) {
mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
                } 
            }
          ...  
        }
        ...
    }

上面两部分主要是简述埋炸弹,拆炸弹,最后说下如果触发引爆炸弹的,其实也就是造成最后弹出ANR弹窗的,很简单主要是看sendMessageDelayed发送的SERVICE_TIMEOUT_MSG的处理情况即可。主要是看ActivityManagerService中的MainHandler的handleMessage方法

 final class MainHandler extends Handler {
        public MainHandler(Looper looper) {
            super(looper, null, true);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            ...
            case SERVICE_TIMEOUT_MSG: {
                ...
                //可以看到会执行ActiveService的serviceTimeout方法
                mServices.serviceTimeout((ProcessRecord)msg.obj);
            } break;
           ...
        } 
 }    

//serviceTimeout中会生成anrMessage,最后通过mAm.mAppErrors.appNotResponding
 void serviceTimeout(ProcessRecord proc) {
        String anrMessage = null;

        synchronized(mAm) {
            if (proc.executingServices.size() == 0 || proc.thread == null) {
                return;
            }
            final long now = SystemClock.uptimeMillis();
            final long maxTime =  now -
                    (proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
            ServiceRecord timeout = null;
            long nextTime = 0;
            for (int i=proc.executingServices.size()-1; i>=0; i--) {
                ServiceRecord sr = proc.executingServices.valueAt(i);
                if (sr.executingStart < maxTime) {
                    timeout = sr;
                    break;
                }
                if (sr.executingStart > nextTime) {
                    nextTime = sr.executingStart;
                }
            }
            if (timeout != null && mAm.mLruProcesses.contains(proc)) {
                Slog.w(TAG, "Timeout executing service: " + timeout);
                StringWriter sw = new StringWriter();
                PrintWriter pw = new FastPrintWriter(sw, false, 1024);
                pw.println(timeout);
                timeout.dump(pw, "    ");
                pw.close();
                mLastAnrDump = sw.toString();
                mAm.mHandler.removeCallbacks(mLastAnrDumpClearer);
                mAm.mHandler.postDelayed(mLastAnrDumpClearer, LAST_ANR_LIFETIME_DURATION_MSECS);
                anrMessage = "executing service " + timeout.shortName;
            } else {
                Message msg = mAm.mHandler.obtainMessage(
                        ActivityManagerService.SERVICE_TIMEOUT_MSG);
                msg.obj = proc;
                mAm.mHandler.sendMessageAtTime(msg, proc.execServicesFg
                        ? (nextTime+SERVICE_TIMEOUT) : (nextTime + SERVICE_BACKGROUND_TIMEOUT));
            }
        }

        if (anrMessage != null) {
           //调用appNotResponding,输出anrMessage
            mAm.mAppErrors.appNotResponding(proc, null, null, false, anrMessage);
        }
    }
    //最后ANR的Dialog是通过handleShowAnrUi方法执行弹出

总结:

  1. 客户端(App进程)向中控系统(system_server进程)发起启动服务的请求
  2. 中控系统派出一名空闲的通信员(binder_1线程)接收该请求,紧接着向组件管家(ActivityManager线程)发送消息,埋下定时炸弹
  3. 通讯员1号(binder_1)通知工地(service所在进程)的通信员准备开始干活
  4. 通讯员3号(binder_3)收到任务后转交给包工头(main主线程),加入包工头的任务队列(MessageQueue)
  5. 包工头经过一番努力干完活(完成service启动的生命周期),然后等待SharedPreferences(简称SP)的持久化;
  6. 包工头在SP执行完成后,立刻向中控系统汇报工作已完成
  7. 中控系统的通讯员2号(binder_2)收到包工头的完工汇报后,立刻拆除炸弹。如果在炸弹倒计时结束前拆除炸弹则相安无事,否则会引发爆炸(触发ANR)

BroadcastReceiver超时机制

broadcast跟service超时机制大抵相同,对于静态注册的广播在超时检测过程需要检测SP

静态广播接收器

动态广播接收器

如果是动态广播,或者静态广播没有正在执行持久化操作的SP任务,则不需要经过“queued-work-looper”线程中转,而是直接向中控系统汇报,流程更为简单,

可见,只有XML静态注册的广播超时检测过程会考虑是否有SP尚未完成,动态广播并不受其影响。SP的apply将修改的数据项更新到内存,然后再异步同步数据到磁盘文件,因此很多地方会推荐在主线程调用采用apply方式,避免阻塞主线程,但静态广播超时检测过程需要SP全部持久化到磁盘,如果过度使用apply会增大应用ANR的概率

在启动广播的流程中,通过调用processNextBroadcast来处理广播, 主要流程是先处理并行广播再处理当前的有序广播,最后获取并处理下条有序广播。

final void processNextBroadcast(boolean fromMsg) {
    synchronized(mService) {
        ...
        //part 2: 处理当前有序广播
        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))) {
                    //当广播处理时间超时,则强制结束这条广播,直接引爆炸弹
                    broadcastTimeoutLocked(false);
                    ...
                }
            }
            if (r.receivers == null || r.nextReceiver >= numReceivers
                    || r.resultAbort || forceReceive) {
                if (r.resultTo != null) {
                    //处理广播消息消息
                    performReceiveLocked(r.callerApp, r.resultTo,
                        new Intent(r.intent), r.resultCode,
                        r.resultData, r.resultExtras, false, false, r.userId);
                    r.resultTo = null;
                }
                //拆炸弹【见小节3.2.2】
                cancelBroadcastTimeoutLocked();
            }
        } while (r == null);
        ...

        //part 3: 获取下条有序广播,并为它埋下超时炸弹
        r.receiverTime = SystemClock.uptimeMillis();
        if (!mPendingBroadcastTimeoutMessage) {
            long timeoutTime = r.receiverTime + mTimeoutPeriod;
            //埋炸弹【见小节3.1.2】
            setBroadcastTimeoutLocked(timeoutTime);
        }
        ...
    }
}
  1. 首先在part3的过程中setBroadcastTimeoutLocked(timeoutTime)设置超时广播消息;
  2. 然后在part2根据广播处理情况来处理:
    • 当广播接收者等待时间过长,则调用broadcastTimeoutLocked(false);
    • 当执行完广播,则调用cancelBroadcastTimeoutLocked;
final void setBroadcastTimeoutLocked(long timeoutTime) {
    if (! mPendingBroadcastTimeoutMessage) {
        Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this);
        mHandler.sendMessageAtTime(msg, timeoutTime);
        mPendingBroadcastTimeoutMessage = true;
    }
}

broadcast跟service超时机制大抵相同,但有一个非常隐蔽的技能点,那就是通过静态注册的广播超时会受SharedPreferences(简称SP)的影响。广播是否考虑SP的情况取决于如下代码:

public final void finish() {
    if (mType == TYPE_COMPONENT) {
        final IActivityManager mgr = ActivityManager.getService();
        if (QueuedWork.hasPendingWork()) {
            //当SP有未同步到磁盘的工作,则需等待其完成,才告知系统已完成该广播
            QueuedWork.queue(new Runnable() {
                public void run() {
                    sendFinished(mgr);
                }
            }, false);
        } else {
            sendFinished(mgr);
        }
    } else if (mOrderedHint && mType != TYPE_UNREGISTERED) {
        final IActivityManager mgr = ActivityManager.getService();
        sendFinished(mgr);
    }
}

可见,只有XML静态注册的广播超时检测过程会考虑是否有SP尚未完成,动态广播并不受其影响。

移除广播超时消息cancelBroadcastTimeoutLocked

final void cancelBroadcastTimeoutLocked() {
    if (mPendingBroadcastTimeoutMessage) {
        mHandler.removeMessages(BROADCAST_TIMEOUT_MSG, this);//移除广播超时消息BROADCAST_TIMEOUT_MSG
        mPendingBroadcastTimeoutMessage = false;
    }
}

引爆炸弹BroadcastHandler.handleMessage

private final class BroadcastHandler extends Handler {
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case BROADCAST_TIMEOUT_MSG: {
                synchronized (mService) {
                    //【见小节3.3.2】
                    broadcastTimeoutLocked(true);
                }
            } break;
            ...
        }
        ...
    }
}
//fromMsg = true
final void broadcastTimeoutLocked(boolean fromMsg) {
    if (fromMsg) {
        mPendingBroadcastTimeoutMessage = false;
    }

    if (mOrderedBroadcasts.size() == 0) {
        return;
    }

    long now = SystemClock.uptimeMillis();
    BroadcastRecord r = mOrderedBroadcasts.get(0);
    if (fromMsg) {
        if (mService.mDidDexOpt) {
            mService.mDidDexOpt = false;
            long timeoutTime = SystemClock.uptimeMillis() + mTimeoutPeriod;
            setBroadcastTimeoutLocked(timeoutTime);
            return;
        }

        if (!mService.mProcessesReady) {
            return; //当系统还没有准备就绪时,广播处理流程中不存在广播超时
        }

        long timeoutTime = r.receiverTime + mTimeoutPeriod;
        if (timeoutTime > now) {
            //如果当前正在执行的receiver没有超时,则重新设置广播超时
            setBroadcastTimeoutLocked(timeoutTime);
            return;
        }
    }

    BroadcastRecord br = mOrderedBroadcasts.get(0);
    if (br.state == BroadcastRecord.WAITING_SERVICES) {
        //广播已经处理完成,但需要等待已启动广播执行完成。当等待足够时间,则处理下一条广播。
        br.curComponent = null;
        br.state = BroadcastRecord.IDLE;
        processNextBroadcast(false);
        return;
    }

    r.receiverTime = now;
    //当前BroadcastRecord的anr次数执行加1操作
    r.anrCount++;

    if (r.nextReceiver <= 0) {
        return;
    }
    ...

    Object curReceiver = r.receivers.get(r.nextReceiver-1);
    //查询App进程
    if (curReceiver instanceof BroadcastFilter) {
        BroadcastFilter bf = (BroadcastFilter)curReceiver;
        if (bf.receiverList.pid != 0
                && bf.receiverList.pid != ActivityManagerService.MY_PID) {
            synchronized (mService.mPidsSelfLocked) {
                app = mService.mPidsSelfLocked.get(
                        bf.receiverList.pid);
            }
        }
    } else {
        app = r.curApp;
    }

    if (app != null) {
        anrMessage = "Broadcast of " + r.intent.toString();
    }

    if (mPendingBroadcast == r) {
        mPendingBroadcast = null;
    }

    //继续移动到下一个广播接收者
    finishReceiverLocked(r, r.resultCode, r.resultData,
            r.resultExtras, r.resultAbort, false);
    scheduleBroadcastsLocked();

    if (anrMessage != null) {
        // [见小节3.3.3]
        mHandler.post(new AppNotResponding(app, anrMessage));
    }
}
  1. mOrderedBroadcasts已处理完成,则不会anr;
  2. 正在执行dexopt,则不会anr;
  3. 系统还没有进入ready状态(mProcessesReady=false),则不会anr;
  4. 如果当前正在执行的receiver没有超时,则重新设置广播超时,不会anr;

6、ANR的信息收集

无论是四大组件或者进程发生ANR都会调用AppErrors中的appNotResponding方法。

    final void appNotResponding(ProcessRecord app, ActivityRecord activity,
        ...
        //获取anr时间   
        long anrTime = SystemClock.uptimeMillis();
        if (ActivityManagerService.MONITOR_CPU_USAGE) {
            //更新CPU统计信息
            mService.updateCpuStatsNow();
        }
        synchronized (mService) {
            // PowerManager.reboot() 会阻塞很长时间,因此忽略关机时的ANR
            if (mService.mShuttingDown) {
                return;
            } else if (app.notResponding) {
                return;
            } else if (app.crashing) {
                return;
            } else if (app.killedByAm) {
                return;
            } else if (app.killed) {
                return;
            }
            //记录ANR到EventLog
            EventLog.writeEvent(EventLogTags.AM_ANR, app.userId, app.pid,
                    app.processName, app.info.flags, annotation);

            // 将当前进程添加到firstPids
            firstPids.add(app.pid);

            // 是否是静默ANR
            isSilentANR = !showBackground && !isInterestingForBackgroundTraces(app);
        // 记录ANR输出到main log
        StringBuilder info = new StringBuilder();
        info.setLength(0);
        info.append("ANR in ").append(app.processName);
        if (activity != null && activity.shortComponentName != null) {
            info.append(" (").append(activity.shortComponentName).append(")");
        }
        info.append("\n");
        info.append("PID: ").append(app.pid).append("\n");
        if (annotation != null) {
            info.append("Reason: ").append(annotation).append("\n");
        }
        if (parent != null && parent != activity) {
            info.append("Parent: ").append(parent.shortComponentName).append("\n");
        }

        ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true);

        ...
        //将traces文件 和 CPU使用率信息保存到dropbox,即data/system/dropbox目录
        mService.addErrorToDropBox("anr", app, app.processName, activity, parent, annotation, cpuInfo, tracesFile, null);
        synchronized (mService) {
            mService.mBatteryStatsService.noteProcessAnr(app.processName, app.uid);
            //后台ANR的情况, 则直接杀掉                
            if (isSilentANR) {
                app.kill("bg anr", true);
                return;
            }

            //设置app的ANR状态,查询错误报告receiver
            makeAppNotRespondingLocked(app,
                    activity != null ? activity.shortComponentName : null,
                    annotation != null ? "ANR " + annotation : "ANR",
                    info.toString());

            //弹出ANR对话框
            Message msg = Message.obtain();
            HashMap<String, Object> map = new HashMap<String, Object>();
            msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG;
            msg.obj = map;
            msg.arg1 = aboveSystem ? 1 : 0;
            map.put("app", app);
            if (activity != null) {
                map.put("activity", activity);
            }
            //向ui线程发送,内容为SHOW_NOT_RESPONDING_MSG的消息
            mService.mUiHandler.sendMessage(msg);
        }
    }
  1. 输出ANR Reason信息到EventLog. 也就是说ANR触发的时间点最接近的就是EventLog中输出的am_anr信息;
  2. 收集并输出重要进程列表中的各个线程的traces信息,该方法较耗时; 【见小节2】
  3. 输出当前各个进程的CPU使用情况以及CPU负载情况;
  4. 将traces文件和 CPU使用情况信息保存到dropbox,即data/system/dropbox目录
  5. 根据进程类型,来决定直接后台杀掉,还是弹框告知用户

7、如何避免ANR

  • 1、避免在主线程上进行复杂耗时的操作,比如说发送接收网络数据/进行大量计算/操作数据库/读写文件等。这个可以通过使用多线程来实现。

  • 2、避免多线程操作的死锁,导致主线程等待超时。

  • 3、在四大组件中生命周期方法不要执行耗时操作,如果比较耗时建议使用多线程实现。


   转载规则


《Android中ANR分析以及出现场景》 mikyou 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
Dart语法篇之集合的使用与源码解析(二) Dart语法篇之集合的使用与源码解析(二)
简述: 我们将继续Dart语法的第二篇集合,虽然集合在第一篇中已经介绍的差不多,但是在这篇文章中将会更加全面介绍有关Dart中的集合,因为之前只是介绍了dart:core包中的List、Set、Map,实际上在dart中还提供一个非常丰富的
2019-10-31
下一篇 
Android中卡顿分析和优化 Android中卡顿分析和优化
卡顿分析和优化我们都知道手机目前最合适的显示60fps, 也就是每秒刷新60帧,这也是也是绝大部分Android设备设置的调试频率。那么绘制1帧的时间大概是16.6ms左右。一般情况下只要在16ms内完成界面的刷新就可以展示出流畅的画面.
2019-10-27