anr日志获取

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/fwt336/article/details/82888401

参考一下链接整理出来的代码:

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资源来创建和执行该进程。

猜你喜欢

转载自blog.csdn.net/fwt336/article/details/82888401
ANR