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工具。