在开发中异常是难以避免的,开发过程中我们可以通过Logcat查看异常信息快速定位异常的原因进行处理,但是当app给测试或者上线后遇到一些难以重现的bug时,我们就很难找出问题所在了,所以需要捕获未捕获的异常。java中给我们提供了UncaughtExceptionHandler这个接口,实现这个接口就可以捕获未捕获的异常信息了。
捕获处理未捕获的异常
public class MyUncaughtException implements Thread.UncaughtExceptionHandler {
@SuppressLint("StaticFieldLeak")
private static MyUncaughtException instance;
private Context mContext;
/**
* 获取单例
*
* @return 捕获异常类
*/
public static MyUncaughtException getInstance() {
if (instance == null) {
synchronized (MyUncaughtException.class) {
if (instance == null) {
instance = new MyUncaughtException();
}
}
}
return instance;
}
/**
* 初始化
*
* @param context 上下文对象
*/
public void init(Context context) {
this.mContext = context;
Thread.setDefaultUncaughtExceptionHandler(this);
}
/**
* 捕获的异常
*
* @param thread 线程
* @param throwable 异常
*/
@Override
public void uncaughtException(Thread thread, Throwable throwable) {
try {
StringWriter sw = new StringWriter();
PrintWriter err = new PrintWriter(sw);
Field[] fields = Build.class.getFields();
for (Field f : fields) {
sw.write(f.getName() + ":" + f.get(null) + "\n");// 静态属性
}
throwable.printStackTrace(err);
String errorLog = sw.toString();
@SuppressLint("SimpleDateFormat")
SimpleDateFormat pathFormat = new SimpleDateFormat("yyyy-MM-dd");
String datePath = pathFormat.format(new Date());
//保存到本地
String filePath = mContext.getExternalCacheDir() + "/Error/" + datePath + "/";
String fileName = "log.txt";
writeTxtToFile(errorLog, filePath, fileName);
sw.close();
err.close();
e.printStackTrace();
} catch (IllegalAccessException | IOException e) {
e.printStackTrace();
}
}
// 将字符串写入到文本文件中
private void writeTxtToFile(String strcontent, String filePath, String fileName) {
makeFilePath(filePath, fileName);
String strFilePath = filePath + fileName;
// 每次写入时,都换行写
String strContent = strcontent + "\r\n";
try {
File file = new File(strFilePath);
if (!file.exists()) {
file.getParentFile().mkdirs();
file.createNewFile();
}
RandomAccessFile raf = new RandomAccessFile(file, "rwd");
raf.seek(file.length());
raf.write(strContent.getBytes());
raf.close();
} catch (Exception e) {
e.printStackTrace();
}
}
// 生成文件
private void makeFilePath(String filePath, String fileName) {
File file = null;
makeRootDirectory(filePath);
try {
file = new File(filePath + fileName);
if (!file.exists()) {
file.createNewFile();
}
} catch (Exception e) {
e.printStackTrace();
}
}
// 生成文件夹
private static void makeRootDirectory(String filePath) {
File file = null;
try {
file = new File(filePath);
if (!file.exists()) {
file.mkdirs();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
使用
放到自定义的Application中初始化即可。
MyUncaughtException.getInstance().init(getApplicationContext());
虽然这样可以捕获到未捕获的异常,当异常在子线程产生时被我们捕获处理,但是当异常产生在主线程时我们发现app此时就会被crash或者导致ANR,那么怎么才能使我们的应用程序不会崩溃呢,我在简书上看到一个crash防护的思路
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
//主线程异常拦截
while (true) {
try {
Looper.loop();
} catch (Throwable e) {
//主线程的异常会从这里抛出
}
}
}
});
});
这段代码的原理作者也做出了详细的解释,我就不画蛇添足了,但是如果应用程序ANP黑屏的时候会导致app无反应,还是需要将app杀死,具体请看原文。