为什么要线上收集崩溃信息?
我们的app上线后后有可能会出现测试阶段没有出现的bug导致崩溃,我们不能及时获取到崩溃的日志及时修复,这时就需要在应用崩溃的时候把崩溃的日志上传到服务器以便我们对崩溃的情况进行分析,当然这里说的是Java 的 UnChecked Exception,Native的 Exception这里不进行考虑。
怎样收集发生异常时的信息
当应用崩溃时,默认会调用Thread类中设置的一个UncaughtExceptionHandler中的uncaughtException方法,所以我们只要实现一个UncaughtExceptionHandler对象,在其对应方法中实现对崩溃信息的保存或者上传即可。
具体的设置过程
- 一般我们会监控UI线程的崩溃信息,所以需要在Application的onCreate()中将我们实现的UncaughtExceptionHandler对象设置进去
Thread.setDefaultUncaughtExceptionHandler(new CrashHandler(Thread.getDefaultUncaughtExceptionHandler()));
UnhandledExceptionHandler即为我们实现的可以自定义捕获异常的类,具体实现如下:
public class CrashHandler {
public static class UnhandledExceptionHandler implements Thread.UncaughtExceptionHandler {
private Thread.UncaughtExceptionHandler uncaughtExceptionHandler;
public CrashHandler(Thread.UncaughtExceptionHandler uncaughtExceptionHandler) {
this.uncaughtExceptionHandler = uncaughtExceptionHandler;
}
@Override
public void uncaughtException(Thread thread, Throwable ex) {
try {
//收集崩溃信息
getStackTraceInfo(ex);
}finally {
//走Android默认的崩溃处理逻辑
uncaughtExceptionHandler.uncaughtException(thread, ex);
}
}
}
/**
* 收集异常信息
* @param throwable
*/
private static void getStackTraceInfo(Throwable throwable) {
}
我们看到在CrashHandler的构造方法中传回了Thread.getDefaultUncaughtExceptionHandler(),这是为了在我们处理完异常信息后系统可以正常的执行崩溃的逻辑。在未捕获的异常发生时,我们就可以在getStackTraceInfo() 中获取崩溃的信息,进行保存或者上传。在此处还可以使用以下多种方式获取当前设备的多种信息以便分析:
- 设备信息
使用PackageManager获取PackageInfo,记录版本名称和版本号。
使用android.os.Build.class.getDeclaredFields()获取设备信息。
- 内存信息
使用ActivityManager获取ActivityManager.MemoryInfo。
ActivityManager.getMemoryClass():在当前设备中,应用可使用最大内存,单位MB。
ActivityManager.getLargeMemoryClass():在manifest中设置android:largeHeap=”true”时,应用可使用最大内存,单位MB。
MemoryInfo.availMem:系统剩余内存
MemoryInfo.lowMemory:系统是否处于低内存运行
MemoryInfo.threshold:当系统剩余内存低于该值时处于低内存运行
- 状态
MemoryInfo.totalMem:系统总内存
使用android.os.Debug.MemoryInfo获取进程的内存统计数据,以KB为单位。
android.os.Debug.dumpHprofData():转储dump文件到手机,可使用MAT分析崩溃时应用内存状况。
Debug.getNativeHeapAllocatedSize():native堆已分配内存,字节为单位。
Debug.getNativeHeapFreeSize():native堆空闲内存,字节为单位。
Debug.getNativeHeapSize():native堆总内存大小,字节为单位。
Debug.MemoryInfo.dalvikPrivateDirty:The private dirty pages used by dalvik.
Debug.MemoryInfo.dalvikPss:The proportional set size for dalvik.
Debug.MemoryInfo.dalvikSharedDirty:The shared dirty pages used by dalvik.
Debug.MemoryInfo.nativePrivateDirty:The private dirty pages used by the native heap.
Debug.MemoryInfo.nativePss:The proportional set size for the native heap.
Debug.MemoryInfo.nativeSharedDirty:The shared dirty pages used by the native heap.
Debug.MemoryInfo.otherPrivateDirty:The private dirty pages used by everything else.
Debug.MemoryInfo.otherPss:The proportional set size for everything else.
Debug.MemoryInfo.otherSharedDirty:The shared dirty pages used by everything else.
使用Runtime.getRuntime()获取虚拟机内存信息,字节为单位。
Runtime.getRuntime().maxMemory():虚拟机可以使用最大内存
Runtime.getRuntime().freeMemory():运行程序可使用空闲内存
Runtime.getRuntime().totalMemory():运行程序可使用总内存大小,当这个值接近maxMemory()时可以发生内存溢出。
- Activity栈
ActivityManager.getRunningTasks(1)可获得当前运行任务。
RunningTaskInfo对应运行任务。
RunningTaskInfo.id:任务id
RunningTaskInfo.description:任务描述
RunningTaskInfo.numActivities:Activity数量
RunningTaskInfo.baseActivity:栈底Activity
RunningTaskInfo.topActivity:栈顶Activity
更便利的方法
如果我们的应用接入了友盟或者bugly等,可以直接使用这些库实现崩溃信息的上传,可以在其网页上直观的查看