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