赞
踩
Application Not Responding,字面意思就是应用无响应,稍加解释就是用户的一些操作无法从应用中获取反馈;在实际的应用中应当去避免这种现象,虽然它暂时不会造成应用崩溃,但是却极大地损坏用户体验;
出现ANR之后一个直观的现象就是系统会展示出一个ANR对话框,如图:
Android系统中,ActivityManagerService(简称AMS)和WindowManagerService(简称WMS)会检测APP的响应时间,如果APP在特定时间无法处理相应屏幕触摸或键盘输入事件,或者特定时间没有处理完毕,就会出现ANR;
那么哪些场景会造成ANR呢?
因此避免以上四种情况就是解决ANR的关键;
导致ANR无响应的常见原因:
解决办法:避免死锁的出现,使用子线程来处理耗时操作或阻塞任务。尽量避免在主线程query provider、不要滥用SharePreferenceS
解决办法:文件读写或数据库操作放在子线程异步操作。
解决办法:
AndroidManifest.xml
文件<applicatiion>中可以设置android:largeHeap="true"
,以此增大App使用内存。不过不建议使用此法,从根本上防止内存泄漏,优化内存使用才是正道。
各大组件生命周期中也应避免耗时操作,注意BroadcastReciever的onRecieve()、后台Service和ContentProvider也不要执行太长时间的任务。
ANR时展示给用户什么内容,源码分析ANR做了哪些事情,知道ANR做了什么,更有助于我们分析ANR;
在Activity内为Button添加点击事件,点击时让主线程休眠30秒,在连续点击几次Button按钮;
- Button.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- // 这是Android提供线程休眠函数,与Thread.sleep()最大的区别是
- // 该使用该函数不会抛出InterruptedException异常。
- SystemClock.sleep(30 * 1000);
- }
- });
在点击Button按钮第一次以后,在连续点击几次Button按钮,大概七八秒,终于弹出ANR异常;
产生ANR时Logcat会同时如下内容:
10-15 01:47:20.975 4038-4046/com.gome.childrenmanager I/art: Thread[5,tid=4046,WaitingInMainSignalCatcherLoop,Thread*=0xaf00d400,peer=0x12c00080,"Signal Catcher"]: reacting to signal 3
10-15 01:47:20.985 4038-4046/com.gome.childrenmanager I/art: Wrote stack traces to '/data/anr/traces.txt'
10-15 01:47:45.820 4038-4038/com.gome.childrenmanager I/Choreographer: Skipped 42 frames! The application may be doing too much work on its main thread.
可以看到Logcat清晰的记录ANR发生的时间,以及线程的tid和一句话概括的原因:WaitingInMainSignalCatcherLoop主线程等待异常,最后一句话 Skipped 42 frames! The application may be doing too much work on its main thread.告知主线程被阻塞导致帧画面无法刷新;
Wrote stack traces to '/data/anr/traces.txt
通过Logcat输出日志看到会将ANR信息写入traces.txt文件,traces.txt文件是一个ANR记录文件,用于开发人员调试分析ANR产生原因,目录在/data/anr中,可以通过adb pull命令导出:
adb pull /data/anr/traces.txt C:\anr
adb命令默认在Android SDK下platform-tools目录,没有配置adb环境变量的需要进入platform-tools目录执行adb命令;
不指定traces.txt导出默认,默认导出到adb所在命令目录;后面对如何分析traces.txt这个文件做详细描述;
最后来看看做出上述反应的源代码,这部分代码位于ProcessRecord类中;
ActivityManagerService监听到ANR信息,调用AnrHelper下的方法appNotResponding(),AnrHelper在后台开启一个独立的线程去处理ANR消息,以便缩短处理时间;
- AnrHelper
- private class AnrConsumerThread extends Thread {
- @Override
- public void run() {
- AnrRecord r;
- r.appNotResponding(onlyDumpSelf);
-
- }
- }
-
- private static class AnrRecord {
- final ProcessRecord mApp;
- void appNotResponding(boolean onlyDumpSelf) {
- mApp.appNotResponding(mActivityShortComponentName, mAppInfo,
- mParentShortComponentName, mParentProcess, mAboveSystem, mAnnotation,
- onlyDumpSelf);
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
ProcessRecord.appNotResponding()
- ProcessRecord.java
- void appNotResponding(String activityShortComponentName, ApplicationInfo aInfo,
- String parentShortComponentName, WindowProcessController parentProcess,
- boolean aboveSystem, String annotation, boolean onlyDumpSelf) {
- ArrayList<Integer> firstPids = new ArrayList<>(5);
- SparseArray<Boolean> lastPids = new SparseArray<>(20);
-
- mWindowProcessController.appEarlyNotResponding(annotation, () -> kill("anr",
- ApplicationExitInfo.REASON_ANR, true));
-
- long anrTime = SystemClock.uptimeMillis();
-
- if (isMonitorCpuUsage()) {
- //第一次,更新CPU统计信息
- mService.updateCpuStatsNow();
- }
-
- final boolean isSilentAnr;
- synchronized (mService) {
- // 某些特定情况下忽略本次ANR,比如系统关机,比如该进程已经处于anr状态或者crash状态
- if (mService.mAtmInternal.isShuttingDown()) {
- Slog.i(TAG, "During shutdown skipping ANR: " + this + " " + annotation);
- return;
- } else if (isNotResponding()) {
- Slog.i(TAG, "Skipping duplicate ANR: " + this + " " + annotation);
- return;
- } else if (isCrashing()) {
- Slog.i(TAG, "Crashing app skipping ANR: " + this + " " + annotation);
- return;
- } else if (killedByAm) {
- Slog.i(TAG, "App already killed by AM skipping ANR: " + this + " " + annotation);
- return;
- } else if (killed) {
- Slog.i(TAG, "Skipping died app ANR: " + this + " " + annotation);
- return;
- }
-
- //为了防止多次对相同APP的anr执行重复代码,在此处标注记录,属于上面的特定情况中的一种
- setNotResponding(true);
-
- // 记录ANR信息到Event Log中
- EventLog.writeEvent(EventLogTags.AM_ANR, userId, pid, processName, info.flags,
- annotation);
-
- //将当前进程添加到firstPids
- firstPids.add(pid);
-
- // 如果它是一个后台的ANR或者仅仅请求导出他自己,不需要加入其它PIDS
- isSilentAnr = isSilentAnr();
- if (!isSilentAnr && !onlyDumpSelf) {
- int parentPid = pid;
- if (parentProcess != null && parentProcess.getPid() > 0) {
- parentPid = parentProcess.getPid();
- }
- if (parentPid != pid) firstPids.add(parentPid);
- //将system_server进程添加到firstPids
- if (MY_PID != pid && MY_PID != parentPid) firstPids.add(MY_PID);
- // 添加所有进程到firstpids中
- for (int i = getLruProcessList().size() - 1; i >= 0; i--) {
- ProcessRecord r = getLruProcessList().get(i);
- if (r != null && r.thread != null) {
- int myPid = r.pid;
- if (myPid > 0 && myPid != pid && myPid != parentPid && myPid != MY_PID) {
- if (r.isPersistent()) {
- firstPids.add(myPid); //将persistent进程添加到firstPids
- if (DEBUG_ANR) Slog.i(TAG, "Adding persistent proc: " + r);
- } else if (r.treatLikeActivity) {
- firstPids.add(myPid);
- if (DEBUG_ANR) Slog.i(TAG, "Adding likely IME: " + r);
- } else { //其它进程添加到lastPids
- lastPids.put(myPid, Boolean.TRUE);
- if (DEBUG_ANR) Slog.i(TAG, "Adding ANR proc: " + r);
- }
- }
- }
- }
- }
- }
-
- // 记录ANR输出到main log
- StringBuilder info = new StringBuilder();
- info.setLength(0);
- info.append("ANR in ").append(processName);
- if (activityShortComponentName != null) {
- info.append(" (").append(activityShortComponentName).append(")");
- }
- info.append("\n");
- info.append("PID: ").append(pid).append("\n");
- if (annotation != null) {
- info.append("Reason: ").append(annotation).append("\n");
- }
- if (parentShortComponentName != null
- && parentShortComponentName.equals(activityShortComponentName)) {
- info.append("Parent: ").append(parentShortComponentName).append("\n");
- }
-
- StringBuilder report = new StringBuilder();
- report.append(MemoryPressureUtil.currentPsiState());
- //创建CPU tracker对象
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。