写在前面:
Android崩溃是我们开发中不可避免的异常处理,通常崩溃都会触发系统的强制关闭对话框,用户点击后系统会强制关闭当前进程,用户体验及其不好。本文就简单介绍下怎么优雅的处理全局异常并实现简单的日志保存,日志上送,以及程序重启。
大神请无视。。
你好,你们程序崩溃了,纳尼?
鬼知道发声了什么...
如果你的项目没有全局异常处理,没有实现日志保存和上送那么恭喜你,你中奖了。
接下来等待你的就是无休止的加班找bug,尽快找到还好,如果找不到问题。领导就要考虑换你了。
1. 什么是UncaughtExceptionHandler
UncaughtExceptionHandler是什么鬼?
官网解释
Interface for handlers invoked when a Thread abruptly terminates due to an uncaught exception.When a thread is about to terminate due to an uncaught exception the Java Virtual Machine will query the thread for its UncaughtExceptionHandler using Thread.getUncaughtExceptionHandler() and will invoke the handler's uncaughtException method, passing the thread and the exception as arguments. If a thread has not had its UncaughtExceptionHandler explicitly set, then its ThreadGroup object acts as its UncaughtExceptionHandler. If the ThreadGroup object has no special requirements for dealing with the exception, it can forward the invocation to the default uncaught exception handler.
大概意思是说:如果一个程序没有捕获异常,java虚拟机调用Thread.getUncaughtExceptionHandler()处理,如果一个线程没有uncaughtexceptionhandler设置,那么它的线程组对象作为其uncaughtexceptionhandler。如果线程组对象具有处理异常的能力,可以在线程组内处理异常,如果没有处理能力也可以转发给默认的捕获异常处理程序。
2. UncaughtExceptionHandler怎么用?
UncaughtExceptionHandler是一个接口,需要创建类来实现这个接口。
- 创建UniException类并实现UncaughtExceptionHandler接口。
- 重写uncaughtException()方法,当UncaughtException发生时会转入该函数来处理。
- 单例模式创建静态getInstance()方法,获取当前UniException类对象。
- 创建init()方法,获取系统默认的UncaughtException处理器,设置本类为程序的默认处理器。
- 创建handleException()方法,自定义错误处理,收集错误日志,上送错误日志。
具体实现如下:
public class UniException implements UncaughtExceptionHandler {
public static final String TAG = "UniException";
// 系统默认的UncaughtException处理类
private Thread.UncaughtExceptionHandler mDefaultHandler;
// UniException实例
private static UniException INSTANCE = new UniException();
// 用来存储设备信息和异常信息
private Map infos = new HashMap();
/** 保证只有一个实例 */
private UniException() {
}
/** 获取UniException实例 ,单例模式 */
public static UniException getInstance() {
if (INSTANCE == null) {
INSTANCE = new UniException();
INSTANCE.init();
}
return INSTANCE;
}
/**
* 初始化
*
* @param context
*/
public void init() {
// 获取系统默认的UncaughtException处理器
mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
// 设置该本类为程序的默认处理器
Thread.setDefaultUncaughtExceptionHandler(this);
}
/**
* 当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) {
LogUtil.e("error : " + e.getMessage());
}
// 退出程序
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(1);
}
}
/**
* 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.
*
* @param ex
* @return true:如果处理了该异常信息;否则返回false.
*/
private boolean handleException(Throwable ex) {
if (ex == null) {
return false;
}
// 使用Toast来显示异常信息
new Thread() {
@Override
public void run() {
Looper.prepare();
Toast.makeText(AppOS.appOS, "很抱歉,程序出现异常,即将退出.", Toast.LENGTH_LONG).show();
Looper.loop();
}
}.start();
// 收集设备参数信息
collectDeviceInfo(AppOS.appOS);
// 保存日志文件
try {
File file = new File(INI.INI_BUS_LOG_FLODER);
if (!file.exists()) {
file.mkdirs();
}
FileOutputStream fout;
fout = new FileOutputStream(new File(INI.INI_BUS_LOG_FLODER + DateUtil.getYourTime("yyyyMMdd") + ".log"),
true);
fout.write((">>>>时间:" + DateUtil.getYourTime(DateUtil.yyyyMMdd_HH_mm_ss) + "\r\n").getBytes("utf-8"));
fout.write(("信息:" + ex.getMessage() + "\r\n").getBytes("utf-8"));
for (int i = 0; i < ex.getStackTrace().length; i++) {
fout.write(("****StackTrace" + i + "\r\n").getBytes("utf-8"));
fout.write(("行数:" + ex.getStackTrace()[i].getLineNumber() + "\r\n").getBytes("utf-8"));
fout.write(("类名:" + ex.getStackTrace()[i].getClassName() + "\r\n").getBytes("utf-8"));
fout.write(("文件:" + ex.getStackTrace()[i].getFileName() + "\r\n").getBytes("utf-8"));
fout.write(("方法:" + ex.getStackTrace()[i].getMethodName() + "\r\n\r\n").getBytes("utf-8"));
}
fout.write(
"--------------------------------\r\n--------------------------------\r\n--------------------------------\r\n"
.getBytes("utf-8"));
fout.close();
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
/**
* 收集设备参数信息
*
* @param ctx
*/
public void collectDeviceInfo(Context ctx) {
try {
PackageManager pm = ctx.getPackageManager();
PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), PackageManager.GET_ACTIVITIES);
if (pi != null) {
String versionName = pi.versionName == null ? "null" : pi.versionName;
String versionCode = pi.versionCode + "";
infos.put("versionName", versionName);
infos.put("versionCode", versionCode);
}
} catch (NameNotFoundException e) {
LogUtil.e("an error occured when collect package info" + e.getMessage());
}
Field[] fields = Build.class.getDeclaredFields();
for (Field field : fields) {
try {
field.setAccessible(true);
infos.put(field.getName(), field.get(null).toString());
LogUtil.e(field.getName() + " : " + field.get(null));
} catch (Exception e) {
LogUtil.e("an error occured when collect crash info" + e.getMessage());
}
}
}
3.定义完成后续咋用?
- 定义类AppOS extends Application
- 在AndroidManifest.xml中注册AppOS
- 注册异常处理UniException.getInstance().init(getApplicationContext());
public class AppOS extends Application {
public static AppOS appOS;
@Override
public void onCreate() {
// TODO
super.onCreate();
appOS = this;
UniException.getInstance().init(getApplicationContext());
}
}
注:后续Android优雅的处理崩溃(二)点击查看