0%

Crash监控

Crash监控

Crash(应用崩溃)是由于代码异常而导致 App 非正常退出,导致应用程序无法继续使用,所有工作都 停止的现象。发生 Crash 后需要重新启动应用(有些情况会自动重启),而且不管应用在开发阶段做得 多么优秀,也无法避免 Crash 发生,特别是在 Android 系统中,系统碎片化严重、各 ROM 之间的差 异,甚至系统Bug,都可能会导致Crash的发生。

在 Android 应用中发生的 Crash 有两种类型,Java 层的 Crash 和 Native 层 Crash。这两种Crash 的监 控和获取堆栈信息有所不同。

Java Crash

Java的Crash监控非常简单,Java中的Thread定义了一个接口: UncaughtExceptionHandler,用于 处理未捕获的异常导致线程的终止(注意:catch了的异常是捕获不到的),当我们的应用crash的时候,就会走 UncaughtExceptionHandler.uncaughtException ,在该方法中可以获取到异常的信息,我们通 过 Thread.setDefaultUncaughtExceptionHandler 该方法来设置线程的默认异常处理器,可以将异常信息保存到本地或者是上传到服务器,方便我们快速的定位问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
public class CrashHandler implements Thread.UncaughtExceptionHandler{
private static final String FILE_NAME_SUFFIX = ".trace";
private static Thread.UncaughtExceptionHandler mDefaultCrashHandler;
private static Context mContext;
private CrashHandler(){
}

public static void init(@NonNull Context context){
//默认为:RuntimeInit#KillApplicationHandler
mDefaultCrashHandler = Thread.getDefaultUncaughtExceptionHandler(); Thread.setDefaultUncaughtExceptionHandler(this);
mContext = context.getApplicationContext();
}
/**
* 当程序中有未被捕获的异常,系统将会调用这个方法 *
* @param t 出现未捕获异常的线程
* @param e 得到异常信息
*/
@Override
public void uncaughtException(Thread t, Throwable e){
try{
//自行处理:保存本地
File file = dealException(e); //上传服务器
//......
} catch (Exception e1){ e1.printStackTrace();
} finally{ //交给系统默认程序处理
if (mDefaultCrashHandler != null){
mDefaultCrashHandler.uncaughtException(t, e);
} }
}

/**
* 导出异常信息到SD卡 *
* @param e
*/
private File dealException(Thread t,Throwable e) throw Exception{
String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new
Date());
File f = new
File(context.getExternalCacheDir().getAbsoluteFile(),"crash_info");
if (!f.exists()) {
f.mkdirs(); }
File crashFile = new File(f, time + FILE_NAME_SUFFIX);
File file = new File(PATH + File.separator + time + FILE_NAME_SUFFIX);
//往文件中写入数据
PrintWriter pw = new PrintWriter(new BufferedWriter(new
FileWriter(file)));
pw.println(time); pw.println("Thread: "+ t.getName()); pw.println(getPhoneInfo()); e.printStackTrace(pw); //写入crash堆栈 pw.close();
return file;
}

private String getPhoneInfo() throws PackageManager.NameNotFoundException{ PackageManager pm = mContext.getPackageManager();
PackageInfo pi = pm.getPackageInfo(mContext.getPackageName(),
PackageManager.GET_ACTIVITIES);
StringBuilder sb = new StringBuilder(); //App版本
sb.append("App Version: "); sb.append(pi.versionName); sb.append("_"); sb.append(pi.versionCode + "\n");
//Android版本号
sb.append("OS Version: "); sb.append(Build.VERSION.RELEASE); sb.append("_"); sb.append(Build.VERSION.SDK_INT + "\n");
//手机制造商
sb.append("Vendor: "); sb.append(Build.MANUFACTURER + "\n");
//手机型号
sb.append("Model: "); sb.append(Build.MODEL + "\n");
//CPU架构
sb.append("CPU: ");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
sb.append(Arrays.toString(Build.SUPPORTED_ABIS)); } else {
sb.append(Build.CPU_ABI); }
return sb.toString(); }
}

NDK Crash

相对于Java的Crash,NDK的错误无疑更加让人头疼,特别是对初学NDK的同学,不说监控,就算是错误堆栈都不知道怎么看。

Linux信号机制

信号机制是Linux进程间通信的一种重要方式,Linux信号一方面用于正常的进程间通信和同步,另一方 面它还负责监控系统异常及中断。当应用程序运行异常时,Linux内核将产生错误信号并通知当前进 程。当前进程在接收到该错误信号后,可以有三种不同的处理方式。

  • 忽略该信号;
  • 捕捉该信号并执行对应的信号处理函数(信号处理程序);
  • 执行该信号的缺省操作(如终止进程);

当Linux应用程序在执行时发生严重错误,一般会导致程序崩溃。其中,Linux专门提供了一类crash信 号,在程序接收到此类信号时,缺省操作是将崩溃的现场信息记录到核心文件,然后终止进程。

一般的出现崩溃信号,Android系统默认缺省操作是直接退出我们的程序。但是系统允许我们给某一个 进程的某一个特定信号注册一个相应的处理函数(signal),即对该信号的默认处理动作进行修改。因 此NDK Crash的监控可以采用这种信号机制,捕获崩溃信号执行我们自己的信号处理函数从而捕获NDK Crash。

BreakPad

Google breakpad是一个跨平台的崩溃转储和分析框架和工具集合,其开源地址是:https://github.co m/google/breakpad。breakpad在Linux中的实现就是借助了Linux信号捕获机制实现的。因为其实现为C++,因此在Android中使用,必须借助NDK工具。