App在客户手中时不时会出现闪退,崩溃等现象。但蛋疼的时有时候无法重现崩溃原因处理。于是,崩溃保存日志出来了,但保存在用户本地也看不到啊,于是,发邮件又来了。效果如图
再说个蛋疼的问题,我在公司Android stadio 2.3.3版本UncaughtExceptionHandler不会跳到这个奔溃的提示页面,但我在家里的Android stadio 3.0.1版本有没问题。默默的问问,2.3.3-3.0.1更新了什么。
好了,不哔哔,上代码。
其中主要的是实现UncaughtExceptionHandler这个接口,这个接口有什么用呢?简单点来说,会针对,某段代码做try … catch 没有catch到的代码,发生异常的时候,就会由setDefaultUncaughtExceptionHandler来处理
/**
* Created by supper on 2017/11/22.
* UncaughtExceptionHandler做全局的catch
* 通常来讲,会针对,某段代码做try … catch 没有catch到的代码,发生异常的时候,就会由setDefaultUncaughtExceptionHandler来处理。
* 当程序崩溃时,由这些代码接管
* 将报错文件报错到本地,超出日志的大小删除掉旧日志
*/
public class CrashHandler implements Thread.UncaughtExceptionHandler {
public static final String TAG = "APP>>CrashHandler";
//系统默认的UncaughtException处理类
private static Thread.UncaughtExceptionHandler mDefaultUncaughtException;
//CarshHandler的单例实例
private static CrashHandler instance;
//程序的Context对象
private Context mContext;
//用来存储设备信息和异常信息
private Map infos = new HashMap();
private String errorLog = "";
private String errorData = "";
private String logName = "error";
private long FileSize = 1024 * 1024 * 10;//1个文件只保存10M的日志,超出重新创建日志文件
Handler handler = new Handler(Looper.getMainLooper());
/** 获取CrashHandler实例 ,单例模式 */
public static CrashHandler getInstance() {
if(instance == null)
instance = new CrashHandler();
return instance;
}
//初始化
public void init(Context context){
Log.d(TAG, "init: 初始化");
mContext = context;
//获取系统默认的UncaughtException处理器
mDefaultUncaughtException = Thread.getDefaultUncaughtExceptionHandler();
//设置该CrashHandler为程序的默认处理器
Thread.setDefaultUncaughtExceptionHandler(this);
cleanLog(3);
}
@Override
public void uncaughtException(Thread thread, Throwable ex) {
if (!handleException(ex) && mDefaultUncaughtException != null) {
//以对代码做try … catch处理的代码,继续让系统默认的异常处理器来处理
mDefaultUncaughtException.uncaughtException(thread, ex);
} else {
//uncaughtException无法调出弹窗,只能跳到Activity,询问用户是否发送邮件
Intent intent = new Intent(mContext, CrashDialog.class);
intent.putExtra("errorData",errorData);
intent.putExtra("errorLog",errorLog);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent);
System.exit(0);
android.os.Process.killProcess(android.os.Process.myPid());
// //App重启处理
// Intent intent = new Intent(mContext, MainActivity.class);
// PendingIntent restartIntent = PendingIntent.getActivity(mContext, 0, intent, Intent.FLAG_ACTIVITY_NEW_TASK);
// //退出程序
// AlarmManager mgr = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
// mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 1000,
// restartIntent); // 1秒钟后重启应用
// System.exit(0);
// android.os.Process.killProcess(android.os.Process.myPid());
}
}
private boolean handleException(Throwable ex){
if(ex == null){
return false;
}
//收集设备参数信息
collectDeviceInfo(mContext);
try{
if(FileUtil.hasSdcard()){
//SD卡可用
saveCrashInfoFile(ex);
}
}catch (Exception e){
Log.e(TAG,e.getMessage());
}
return true;
}
private 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 (PackageManager.NameNotFoundException e) {
Log.e(TAG, "获取版本信息不成功》》" + e);
}
//获取手机的配置信息
Field[] fields = Build.class.getDeclaredFields();
for (Field field : fields) {
try {
field.setAccessible(true);//成员变量为private,故必须进行此操
infos.put(field.getName(), field.get(null).toString());
Log.d(TAG, field.getName() + " : " + field.get(null));
} catch (Exception e) {
Log.e(TAG, "获取配置信息不成功》》", e);
}
}
}
/**
* 保存错误信息到文件中
* @param ex
* @return 返回文件名称,便于将文件传送到服务器
* @throws Exception
*/
private String saveCrashInfoFile(Throwable ex) throws Exception {
Log.d(TAG, "saveCrashInfoFile:错误信息" + ex.getMessage());
errorLog = "";
errorData = "";
Log.d(TAG, "saveCrashInfoFile: gg了,保存报错日志到本地");
StringBuffer sb = new StringBuffer();
try {
sb.append("\n\n\n-------------------------------------------我是开始的分割线---------------------------------------------------\n");
SimpleDateFormat sDateFormat = new SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss");
errorData = sDateFormat.format(new java.util.Date());
sb.append(errorData + "\n");
//Map.Entry 将map里的每一个键值对取出来封装成一个Entry对象在存到一个Set里面
for (Map.Entry entry : infos.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
sb.append(key + "=" + value + "\n");
}
Writer writer = new StringWriter();
PrintWriter printWriter = new PrintWriter(writer);
ex.printStackTrace(printWriter);
Throwable cause = ex.getCause();
while (cause != null) {
cause.printStackTrace(printWriter);
cause = cause.getCause();
}
printWriter.flush();
printWriter.close();
String result = writer.toString();
sb.append(result);
sb.append("-------------------------------------------我是结束的分割线---------------------------------------------------\n\n\n\n");
final String fileName = getFileName();
FileUtil.writeFile(fileName,sb.toString(),true);
e