除去第三方的异常处理,一般APP都有自己的异常处理函数。
自定义自己的异常处理函数很有好处,可以增加自己额外的错误定位信息,也可以在三方处理以外进行自己的额外的操作。
网上例子很多,一般的异常处理无非是这样:
/**
* 当UncaughtException发生时会转入该函数来处理
*/
@Override
public void uncaughtException(Thread thread, Throwable ex) {
if (!handleException(ex) && mDefaultHandler != null) {
//如果用户没有处理则让系统默认的异常处理器来处理
mDefaultHandler.uncaughtException(thread, ex);
} else {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
Log.e(TAG, "error : ", e);
}
//退出程序
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(1);
}
}
先执行自己的处理,根据处理结果和是否存在默认处理函数决定是否执行默认的处理,
如果处理好了直接退出程序。
然而实际当中我们往往集成了一些三方SDK,很多SDK都有自己的异常处理函数。
比如蒲公英,比如友盟。
项目中丧心病狂的同时用了两个。
最近需要把错误信息重新调整一下,就进行了一下研究。
在项目中,蒲公英的异常收集初始化是在自己的异常收集之前,也就是说,我自己异常处理函数里的mDefaultHandler,其实是蒲公英的,而蒲公英的则是系统的。
尝试了几种情况:
1.直接执行自己的操作并kill,功能正常。
2.直接执行default并kill,功能正常。
3.执行postDelay + kill,无法退出程序
4.执行new Thread + kill,功能正常
6.Toast必须new Thread + Looper初始化才能显示
7.用Log4j在处理函数里用同步方式直接写日志后kill,可以写如成功
8.使用new Thread + default 执行kill,无法退出
9.使用new Thread + kill,可以退出
10.使用new Thread + (default + kill 顺序执行),无法退出
11.不初始化蒲公英异常处理直接new Thread + default,可以退出
追踪蒲公英的异常处理代码,如下:
public void uncaughtException(Thread thread, Throwable exception) {
if(com.pgyersdk.conf.a.a == null) {
this.c.uncaughtException(thread, exception);
} else {
(new com.pgyersdk.crash.b(this, exception)).start();
if(!this.a) {
this.c.uncaughtException(thread, exception);
} else {
Process.killProcess(Process.myPid());
System.exit(10);
}
}
}
无非是多了两个判断,但是其中似乎多了一段Thread中执行的代码,其中执行了什么?看不到。
问了蒲公英的技术支持,说是有冲突。必须直接调用他们的异常处理函数。
所以无奈,本想在自己的处理中搞个延时,弹个Toast啥的,现在看来,有点复杂。
看来在接入多个带异常处理的三方SDK时,为了安全起见,如果自定义异常处理,还是不要在线程中调用他们的处理函数,以防冲突。
上面还得出的结论有:
Handler不好使
Toast必须初始化Looper
最后,异常处理的代码变成了这个样子,其实和文章开头的差不多,脸红:
public class XCrashHandler implements Thread.UncaughtExceptionHandler {
private Thread.UncaughtExceptionHandler mDefaultHandler;
private Context mContext;
public XCrashHandler(){
}
public void register(Context context){
this.mContext = context.getApplicationContext();
mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(this);
}
@Override
public void uncaughtException(final Thread thread, final Throwable ex) {
final CharSequence str = getLastWord(ex);
XLog.e(XLog.BASE, str.toString());
if (mDefaultHandler != null){
mDefaultHandler.uncaughtException(thread, ex);
}else{
exit();
}
}
private void showToast(final Context context, final String msg) {
threadDelay(new Runnable() {
@Override
public void run() {
Looper.prepare();
ToastUtil.longToast(context, msg);
Looper.loop();
}
}, 0);
}
private void exit(){
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(0);
}
@Deprecated
private void postDelay(final Runnable runnable, final int delay){
new Handler().postDelayed(runnable, delay);
}
private void threadDelay(final Runnable runnable, final int delay){
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
XLog.e(XLog.BASE, "error : "+ e);
}
runnable.run();
}
}).start();
}
private CharSequence getLastWord(Throwable e){
StringBuffer sb = new StringBuffer();
final String sep = "\n";
sb.append(sep + "-------------------- LAST WORD --------------------" + sep);
sb.append(new Date() + sep);
sb.append(BuildInfoHelper.build());
// sb.append("" + e.toString() + sep);
// StackTraceElement [] stackTrace = e.getStackTrace();
// for (int i = 0; i < stackTrace.length; i++) {
// sb.append(" at " + stackTrace[i].getClassName()
// + "." + stackTrace[i].getMethodName() + "(" + stackTrace[i].getFileName()
// + ":" + stackTrace[i].getLineNumber() + ")"
// + sep);
// }
sb.append(getErrorInfoFromException(e));
return sb;
}
public static String getErrorInfoFromException(Throwable e) {
final String sep = "\n";
try {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
return sep + sw.toString() + sep;
} catch (Exception e2) {
return "bad getErrorInfoFromException";
}
}
private static class BuildInfoHelper{
private static final String fmt = "%15s %s\n";
private static StringBuffer sb;
public static StringBuffer build(){
sb = new StringBuffer();
append("SDK_INT", Build.VERSION.SDK_INT);
append("CODENAME", Build.VERSION.CODENAME);
append("INCREMENTAL", Build.VERSION.INCREMENTAL);
append("RELEASE", Build.VERSION.RELEASE);
append("BOARD", Build.BOARD);
append("BOOTLOADER", Build.BOOTLOADER);
append("BRAND", Build.BRAND);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
append("SUPPORTED_ABIS", Build.SUPPORTED_ABIS);
}else{
append("CPU_ABI", Build.CPU_ABI);
append("CPU_ABI2", Build.CPU_ABI2);
}
append("DEVICE", Build.DEVICE);
append("DISPLAY", Build.DISPLAY);
append("FINGERPRINT", Build.FINGERPRINT);
append("HARDWARE", Build.HARDWARE);
append("HOST", Build.HOST);
append("ID", Build.ID);
append("MANUFACTURER", Build.MANUFACTURER);
append("MODEL", Build.MODEL);
append("PRODUCT", Build.PRODUCT);
append("SERIAL", Build.SERIAL);
append("TAGS", Build.TAGS);
append("TYPE", Build.TYPE);
append("USER", Build.USER);
append("TIME", Build.TIME);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
append("RADIO_VERSION", Build.getRadioVersion());
}
return sb;
}
private static void append(Object ... arg){
sb.append(String.format(fmt, arg));
}
}
}