这是我参与11月更文挑战的第8天,活动详情查看:2021最后一次更文挑战
应用异常
日常开发中总离不开Bug,而bug绝大多数又是崩溃。当应用运行发生严重异常时就会出现Crash崩溃的情况,通常情况为空指针异常、内存溢出等。当发生异常情况时系统就会Kill杀死执行的程序并结束掉进程。
开发者在日常开发中可以通过开发Log查看崩溃日志并修复问题。但在线上环境以及测试遇到崩溃应用闪退,程序停止运行对于开发者而言是无法直接感知和获取到崩溃信息的。这对开发者是极其不友好的情况,有崩溃问题却不知道如何去解决这个Crash。
好在Android提供了处理异常情况的方法。Java提供了Thread.UncaughtExceptionHandler一个全局异常捕获处理器,在Android环境下同样也可使用。
Thread.UncaughtExceptionHandler
Thread.UncaughtExceptionHandler是一个接口方法,处理在程序中未被捕获的异常信息。
但例如开发者自行增加
try-catch则会被开发者自行拦截且不会被异常捕获器收集。
@FunctionalInterface
public interface UncaughtExceptionHandler {
/**
* Method invoked when the given thread terminates due to the
* given uncaught exception.
* <p>Any exception thrown by this method will be ignored by the
* Java Virtual Machine.
* @param t the thread
* @param e the exception
*/
void uncaughtException(Thread t, Throwable e);
}
实现方法
自定义异常获取类
public class CrashHandler implements Thread.UncaughtExceptionHandler{
@Override
public void uncaughtException(Thread t, Throwable e) {
// 全局捕获异常在这里
}
}
初始化方法,先获取当前线程的异常捕获器再将自定义异常捕获器类设置为线程异常捕获器,这样全局异常就由自定义类捕获。
Thread.UncaughtExceptionHandler mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(this);
信息收集
信息收集在回调方法uncaughtException实现,读取错误Throwable转文本内容存储在本地文件或是上传到服务器。
- 错误堆栈信息
final Writer result = new StringWriter();
final PrintWriter printWriter = new PrintWriter(result);
Throwable cause = ex;
while(cause != null){
cause.printStackTrach(printWriter);
cause = cause.getCause();
}
final String stacktraceAsString = result.toString();
printWriter.close();
- 错误线程信息
StringBuilder result = new StringBuilder();
if(thread!=null){
result.append("id=").append(thread.getId()).append("\n");
result.append("name=").append(thread.getName()).append("\n");
result.append("priority=").append(thread.getPriority()).append("\n");
if(t.getThreadGroup()!=null)){
result.append("groupName=").append(thread.getThreadGroup().getName()).append("\n");
}
}
return result.toString();
}
-
系统信息 除了异常信息之外,还需要增加一些额外信息帮助开发者更好排查问题原因。例如一些机型适配问题或是只有在低端机才会发生的错误等。因此可以在异常发生上再额外上报应用运行的系统信息。 设备基础信息获取方法可参考# Android设备基本信息获取
-
内存 此外还能在异常发生时去获取内存使用情况信息也是能提高问题定位能力的。
public static String collectMemInfo(){
final StringBuilder meminfo = new StringBuilder();
BufferedReader bufferedReader = null;
try{
final List<String commandLine = new ArrayList<>();
commandLine.add("dumpsys");
commandLine.add("meminfo");
commandLine.add(Integer.toString(android.os.Process.myPid()));
final Process process = Runtime.getRuntime().exec(commandLine.toArray(new String[commandLine.size()]));
bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()), DEFAULT_BUFFER_SIZE_IN_BYTES);
while(true){
final String line = bufferedReader.readLine();
if(line == null)break;
meminfo.append(line);
meminfo.append("\n");
}
}catch(Exception e){}
try{
if(null!=bufferedReader){
bufferedReader.close();
}catch(Exception e){}
}
return meminfo.toString();
}
Native异常
Native异常对于Android应用来说使用Thread.UncaughtExceptionHandler是无法捕捉到的。C++异常通常情况是野指针或是内存读取越界导致,可以在LogCat看到错误日志信息。但希望方便快速获取到Native层异常并不是特别容易的事情,并不像Android源码就提供异常捕捉器直接获取到。
- 目前已知方法是重构
LogCat日志信息去获取相关堆栈信息,通过开辟一个新进程服务去抓取LogCat信息找到Native异常并保存。 - 集成开源库
xCrash抓取到异常信息。 - 待补充。