参考一下链接整理出来的代码:
https://codezjx.com/2017/08/06/anr-trace-analytics/
https://www.jianshu.com/p/6d855e984b99
http://gityuan.com/2016/07/02/android-anr/
非常感谢以上博主的奉献。
1.ANR发生的条件
输入事件:按钮事件10秒内未响应
前台服务,则超时为SERVICE_TIMEOUT = 20s;
后台服务,则超时为SERVICE_BACKGROUND_TIMEOUT = 200s
前台广播,则超时为BROADCAST_FG_TIMEOUT = 10s;
后台广播,则超时为BROADCAST_BG_TIMEOUT = 60s;
ContentProvider超时为CONTENT_PROVIDER_PUBLISH_TIMEOUT = 10s;
2.查找原因
2.1导出traces.txt文件
到处traces.txt文件: adb pull /data/anr/traces.txt traces.txt
但是,经测试有部分手机无法获取到traces.txt内容,应该是跟手机有关
2.2查看traces.txt文件内容
我们最后一个发生ANR的日志,在traces.txt文件的顶部,一般的内容如下(省略了很多无关信息):
----- pid 8025 at 2018-09-28 18:42:50 -----
Cmd line: com.victor.hello(包名)
"main" prio=5 tid=1 Sleeping
| group="main" sCount=1 dsCount=0 flags=1 obj=0x73e7dbb0 self=0x7b00aa3a00
| sysTid=8025 nice=-10 cgrp=default sched=0/0 handle=0x7b059379b0
| state=S schedstat=( 460020829 914061 307 ) utm=37 stm=9 core=4 HZ=100
| stack=0x7fd4061000-0x7fd4063000 stackSize=8MB
| held mutexes=
at java.lang.Thread.sleep(Native method)(原因)
- sleeping on <0x0600409f> (a java.lang.Object)
at java.lang.Thread.sleep(Thread.java:386)
- locked <0x0600409f> (a java.lang.Object)
at java.lang.Thread.sleep(Thread.java:327)
at com.victor.hello.AidlService$mBind$1.test(AidlService.kt:18)(代码中出错的地方)
at com.victor.hello.MainActivity$mServiceConnection$1.onServiceConnected(MainActivity.kt:31)
at android.app.LoadedApk$ServiceDispatcher.doConnected(LoadedApk.java:1818)
at android.app.LoadedApk$ServiceDispatcher$RunConnection.run(LoadedApk.java:1847)
at android.os.Handler.handleCallback(Handler.java:808)
at android.os.Handler.dispatchMessage(Handler.java:101)
at android.os.Looper.loop(Looper.java:166)
at android.app.ActivityThread.main(ActivityThread.java:7425)
at java.lang.reflect.Method.invoke(Native method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:245)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:921)
----- end 8025 -----
通过日志我们可以很容易看出是由于sleep导致的ANR问题,这是故意设置的。
实际开发中,还会出现由于CPU处于满负荷工作,而没有时间处理当前APP的事务,导致的ANR:
CPU usage from 2180ms to -3231ms ago:
99% 507/system_server: 68% user + 31% kernel / faults: 20539 minor
0.5% 193/logd: 0.1% user + 0.4% kernel / faults: 54 minor
0.2 2506/com.victor.hello: 0.09% user + 0.11% kernel / faults: 1485 minor
0% 208/debuggerd: 0% user + 0% kernel / faults: 3101 minor
0% 667/com.android.systemui: 0% user + 0% kernel / faults: 1786 minor
0% 916/com.android.phone: 0% user + 0% kernel / faults: 1933 minor
0% 630/android.process.media: 0% user + 0% kernel / faults: 1649 minor
0% 1685/kworker/3:2: 0% user + 0% kernel
0% 1974/com.android.defcontainer: 0% user + 0% kernel / faults: 17 minor
0% 2191/kworker/1:2: 0% user + 0% kernel
100% TOTAL: 99% user + 0.8% kernel + 0.1% iowait + 0.0.05% irq + 0.5% softirq
在该条log之后会有CPU usage的信息,表明了CPU在ANR前后的用量(log会表明截取ANR的时间),从各种CPU Usage信息中大概可以分析如下几点:
(1). 如果某些进程的CPU占用百分比较高,几乎占用了所有CPU资源,而发生ANR的进程CPU占用为0%或非常低,则认为CPU资源被占用,进程没有被分配足够的资源,从而发生了ANR。这种情况多数可以认为是系统状态的问题,并不是由本应用造成的
2). 如果发生ANR的进程CPU占用较高,如到了80%或90%以上,则可以怀疑应用内一些代码不合理消耗掉了CPU资源,如出现了死循环或者后台有许多线程执行任务等等原因,这就要结合trace和ANR前后的log进一步分析了。
(3). 如果CPU总用量不高,该进程和其他进程的占用过高,这有一定概率是由于某些主线程的操作就是耗时过长,或者是由于主进程被锁造成的。
3.获取ANR 日志
3.1通过监听traces.txt文件写文件关闭动作
public class AnrService extends Service {
static final String TAG = "heheda";
FileObserver fileObserver = null;
@Override
public void onCreate() {
super.onCreate();
startObserver();
}
private void startObserver() {
String anrFilePaht = "/data/anr/";
// 监听文件写关闭动作
try {
FileObserver fileObserver = new FileObserver(anrFilePaht, FileObserver.CLOSE_WRITE) {
@Override
public void onEvent(int event, @Nullable String path) {
if (path != null) {
String filePaht = "/data/anr/" + path;
// 找到trace.txt文件
if (filePaht.contains("traces")) {
anrStrFilter(path);
}
}
}
};
fileObserver.startWatching();
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "error: " + e.getMessage());
}
}
/**
* 先解析出时间,对一些重复的回调或太频繁的ANR进行过滤
*/
private void anrStrFilter(String path) {
try {
long var2 = -1L;
File var4 = new File(path);
if (var4 != null) {
var2 = var4.lastModified();
}
if (var2 == -1L) {
Log.d(TAG, "trace dump fail could not get time!");
var2 = (new Date()).getTime();
}
if (Math.abs(var2 - System.currentTimeMillis()) < 10_000L) {
Log.d(TAG, "should not process ANR too Fre");
} else {
ActivityManager.ProcessErrorStateInfo processErrorStateInfo = processErrorInfo(this);
if (processErrorStateInfo == null) {
Log.d(TAG, "proc state is unvisiable!");
} else if (processErrorStateInfo.pid != android.os.Process.myPid()) {
Log.e(TAG, "not mind proc!" + processErrorStateInfo.processName);
} else {
Log.e(TAG, "found visiable anr , start to process!");
}
}
} catch (Throwable ex) {
}
}
protected ActivityManager.ProcessErrorStateInfo processErrorInfo(Context context) {
int var2 = 5000;
Log.e(TAG, "to search ARN info !");
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
int var7 = 0;
do {
List<ActivityManager.ProcessErrorStateInfo> list = activityManager.getProcessesInErrorState();
if (list != null) {
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
ActivityManager.ProcessErrorStateInfo processErrorStateInfo = (ActivityManager.ProcessErrorStateInfo) iterator.next();
if (processErrorStateInfo.condition == ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING) {
Log.e(TAG, " I found it !");
StringBuffer stringBuilder = new StringBuffer();
// stringBuilder.append("pid = ${processErrorStateInfo.pid}, \n");
// stringBuilder.append("condition = ${processErrorStateInfo.condition}, \n");
// stringBuilder.append("crashData = " + new String(processErrorStateInfo.crashData) + ", \n"); // 报错
stringBuilder.append("longMsg =" + processErrorStateInfo.longMsg + " \n");
stringBuilder.append("processName =" + processErrorStateInfo.processName + ", \n");
stringBuilder.append("shortMsg = " + processErrorStateInfo.shortMsg + ", \n");
stringBuilder.append("stackTrace = " + processErrorStateInfo.stackTrace + " \n");
stringBuilder.append("tag = " + processErrorStateInfo.tag + ", \n");
// stringBuilder.append("uid = ${processErrorStateInfo.uid}\n");
Log.e(TAG, stringBuilder.toString());
return processErrorStateInfo;
}
}
}
} while (var7++ < var2);
Log.e(TAG, "search anr end !");
return null;
}
@Override
public void onDestroy() {
fileObserver.stopWatching();
super.onDestroy();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
前提条件是,手机必须要root,不然没法监听traces.txt文件
3.2监听ANR广播
经测试发现,只有部分手机可以收到该广播
public static class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(ACTION_ANR)) {
Log.e("anr", "发生ANR了");
}
}
}
3.3集成Bugly等第三方工具
集成第三方工具的好处时,兼容性好,使用简单
4.解决ANR
4.1主线程在做一些耗时的工作
避免在主线程做耗时操作,多使用异步处理。
使用线程池处理耗时操作,不直接new线程;
AsyncTask异步处理耗时操作,可用于更新UI;
使用IntentService处理耗时操作;
另外,可用于异步更新UI的方法有:
AsyncTask,handler.post()和handler.postDelay(),runOnUIThread();甚至是EventBus都可以做到;
4.2主线程与其他线程发生死锁
我们在使用锁的时候,应该很清晰的认识到是否会发生死锁的情况,尽量不一个方法不使用多个锁。
https://blog.csdn.net/fwt336/article/details/82051537
4.3cpu被其他进程占用,该进程没被分配到足够的cpu资源
当手机设备在cpu使用量接近100%时,如果这个时候启动APP,那么会导致APP无法被迅速启动,发生黑屏或卡顿情况,因为cpu无法分配足够的cpu资源来创建和执行该进程。