1. 程式人生 > >anr日誌獲取

anr日誌獲取

參考一下連結整理出來的程式碼:

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資源來建立和執行該程序。