-
简介
app崩溃,这个是大家都会遇到的问题,如果是我们自测,这个easy,我们很容易根据日志找到原因。但是有的时候并不会那么理想。 比如本人要和一个国内电视机厂商合作,因为应用都是内置到系统,并且一个人负责几个app。因为不在同一个城市,每次整机升级的时候都很痛苦。因为对方的测试也的确要测试很多东西。他们基本没时间帮你抓日志。直接把系统日志抛出来,基本都是那种20m以上的日志。这个时候就脑袋疼了。还有一点就是每次崩溃,都有一个提示框出来,也不美观。 让人觉得比较low。所以我们就需要自己处理app整体流程的崩溃。好了,吐槽完毕,上代码。
-
代码分析
1 首先是一个实现了UncaughtExceptionHandler接口的类,这个类来捕获app内部异常。
public class CrashCatchHandler implements Thread.UncaughtExceptionHandler {
private static CrashCatchHandler INSTANCE;
private Context mContext;
private static final String PATH = Environment.getExternalStorageDirectory().getPath() + "/log/";
private CrashCatchHandler() {
}
public static CrashCatchHandler getInstance() {
if (null == INSTANCE) {
synchronized (CrashCatchHandler.class) {
if (null == INSTANCE) {
INSTANCE = new CrashCatchHandler();
}
}
}
return INSTANCE;
}
public void init(Context c) {
//这里的c应该是全局的。
if (c == null) {
System.out.println("the context cann't be empty ");
return;
}
mContext = c;
}
@Override
public void uncaughtException(Thread t, Throwable e) {
//如果是测试,那么只要写入本地日志即可,然后让测试拿到日志就可以。
writeToFile(e);
//在测试的时候无需上报
uploadToServer();
//杀死这个进程避免类似ios直接退出。
Process.killProcess(Process.myPid());
}
/**
* 把错误日志写到文件中。
*/
private void writeToFile(Throwable ex) {
//在这里需要确认具体的存储路径
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
return;
}
File dir = new File(PATH);
if (!dir.exists()) {
dir.mkdirs();
}
String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
File file = new File(PATH + time + ".txt");
try {
PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(file)));
//导出发生异常的时间
pw.println(time);
pw.println();
//导出异常的调用栈信息
ex.printStackTrace(pw);
pw.close();
} catch (Exception e) {
}
}
/**
* 将错误日志上传到服务器
*/
private void uploadToServer() {
}
}
其中最重要的是实现uncaughtException()这个函数,我们通过源码中可以看到,这个thread发生异常的时候会触发。
/**
* Interface for handlers invoked when a <tt>Thread</tt> abruptly
* terminates due to an uncaught exception.
* <p>When a thread is about to terminate due to an uncaught exception
* the Java Virtual Machine will query the thread for its
* <tt>UncaughtExceptionHandler</tt> using
* {@link #getUncaughtExceptionHandler} and will invoke the handler's
* <tt>uncaughtException</tt> method, passing the thread and the
* exception as arguments.
* If a thread has not had its <tt>UncaughtExceptionHandler</tt>
* explicitly set, then its <tt>ThreadGroup</tt> object acts as its
* <tt>UncaughtExceptionHandler</tt>. If the <tt>ThreadGroup</tt> object
* has no
* special requirements for dealing with the exception, it can forward
* the invocation to the {@linkplain #getDefaultUncaughtExceptionHandler
* default uncaught exception handler}.
*
* @see #setDefaultUncaughtExceptionHandler
* @see #setUncaughtExceptionHandler
* @see ThreadGroup#uncaughtException
* @since 1.5
*/
@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);
}
2 实现了这个接口之后,需要注册一下。需要在application的子类中实现。
public class MyApp extends MultiDexApplication {
@Override
public void onCreate(){
super.onCreate();
CrashCatchHandler crashHandler = CrashCatchHandler.getInstance();
crashHandler.init(this);
Thread.setDefaultUncaughtExceptionHandler(crashHandler);
}
}
这样注册之后,就可以是实现捕获程序里面的错误日志了。