package com.doumee.keyCabinet.utils;
|
|
import android.app.AlarmManager;
|
import android.app.PendingIntent;
|
import android.content.Context;
|
import android.content.Intent;
|
import android.content.pm.PackageInfo;
|
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager.NameNotFoundException;
|
import android.os.Build;
|
import android.os.Environment;
|
import android.os.Looper;
|
|
import com.doumee.keyCabinet.ui.guide.GuideActivity;
|
import com.doumee.lib_coremodel.util.SpUtil;
|
|
import java.io.ByteArrayOutputStream;
|
import java.io.IOException;
|
import java.io.PrintStream;
|
import java.io.PrintWriter;
|
import java.io.StringWriter;
|
import java.io.Writer;
|
import java.lang.Thread.UncaughtExceptionHandler;
|
import java.text.SimpleDateFormat;
|
import java.util.Date;
|
import java.util.Properties;
|
|
/**
|
*
|
*
|
* UncaughtExceptionHandler:线程未捕获异常控制器是用来处理未捕获异常的。 如果程序出现了未捕获异常默认情况下则会出现强行关闭对话框
|
* 实现该接口并注册为程序中的默认未捕获异常处理 这样当未捕获异常发生时,就可以做些异常处理操作 例如:收集异常信息,发送错误报告 等。
|
*
|
* UncaughtException处理类,当程序发生Uncaught异常的时候,由该类来接管程序,并记录发送错误报告.
|
*/
|
public class CrashHandler implements UncaughtExceptionHandler {
|
private static final String TAG = "CrashHandler";
|
private static String PATH = Environment.getExternalStorageDirectory().getPath() + "/log/";
|
public static final String FILE_NAME = "crash";
|
//log文件的后缀名
|
private static final String FILE_NAME_SUFFIX = ".txt";
|
|
public static final String CLIENT_VERSION = "clientVersion";
|
|
/** CrashHandler实例 */
|
private static CrashHandler INSTANCE;
|
/** 程序的Context对象 */
|
private Context mContext;
|
/** 系统默认的UncaughtException处理类 */
|
private UncaughtExceptionHandler mDefaultHandler;
|
|
/** 使用Properties来保存设备的信息和错误堆栈信息 */
|
private Properties mDeviceCrashInfo = new Properties();
|
|
/** 保证只有一个CrashHandler实例 */
|
private CrashHandler() {
|
}
|
|
/** 获取CrashHandler实例 ,单例模式 */
|
public static CrashHandler getInstance() {
|
if (INSTANCE == null) {
|
INSTANCE = new CrashHandler();
|
}
|
return INSTANCE;
|
}
|
|
/**
|
* 初始化,注册Context对象, 获取系统默认的UncaughtException处理器, 设置该CrashHandler为程序的默认处理器
|
*
|
* @param ctx
|
*/
|
public void init(Context ctx) {
|
mContext = ctx;
|
//PATH = ctx.getFilesDir().getParentFile().getAbsolutePath()+ "/log/";
|
mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
|
Thread.setDefaultUncaughtExceptionHandler(this);
|
// 收集设备信息
|
collectCrashDeviceInfo(mContext);
|
}
|
|
@Override
|
public void uncaughtException(Thread thread, Throwable ex) {
|
try {
|
//导出异常信息到SD卡中
|
dumpExceptionToSDCard(ex);
|
//System.out.println("重启应用");
|
// 创建一个新的启动意图
|
Intent intent = new Intent(mContext, GuideActivity.class);
|
// 设置FLAG_ACTIVITY_CLEAR_TASK标志位
|
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
// 使用PendingIntent包装启动意图
|
PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT);
|
// 获取AlarmManager实例
|
AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
|
// 在500毫秒后触发重启操作
|
alarmManager.set(AlarmManager.RTC, System.currentTimeMillis() + 10, pendingIntent);
|
|
android.os.Process.killProcess(android.os.Process.myPid());
|
System.exit(1);
|
//这里可以通过网络上传异常信息到服务器,便于开发人员分析日志从而解决bug
|
} catch (IOException e) {
|
e.printStackTrace();
|
System.out.println("错误日志1"+e.getMessage());
|
}
|
//打印出当前调用栈信息
|
ex.printStackTrace();
|
//如果系统提供了默认的异常处理器,则交给系统去结束我们的程序,否则就由我们自己结束自己
|
if (mDefaultHandler != null) {
|
mDefaultHandler.uncaughtException(thread, ex);
|
} else {
|
android.os.Process.killProcess(android.os.Process.myPid());
|
}
|
|
if (!handleException(ex) && mDefaultHandler != null) {
|
// 如果用户没有处理则让系统默认的异常处理器来处理
|
mDefaultHandler.uncaughtException(thread, ex);
|
} else {
|
/*try {
|
Thread.sleep(500);
|
} catch (InterruptedException e) {
|
Log.e(TAG, "error : ", e);
|
}*/
|
/*System.out.println("重启应用");
|
// 创建一个新的启动意图
|
Intent intent = new Intent(mContext, GuideActivity.class);
|
// 设置FLAG_ACTIVITY_CLEAR_TASK标志位
|
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
// 使用PendingIntent包装启动意图
|
PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT);
|
// 获取AlarmManager实例
|
AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
|
// 在500毫秒后触发重启操作
|
alarmManager.set(AlarmManager.RTC, System.currentTimeMillis() + 10, pendingIntent);
|
|
android.os.Process.killProcess(android.os.Process.myPid());
|
System.exit(1);*/
|
/*// 未捕获异常写文件
|
writeCrashInfoToFile(ex);
|
// 退出整个应用
|
if (mDefaultHandler != null) {
|
mDefaultHandler.uncaughtException(thread, ex);
|
} else {
|
System.exit(0);
|
}*/
|
}
|
}
|
|
/**
|
* 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成. 开发者可以根据自己的情况来自定义异常处理逻辑
|
*
|
* @param ex
|
* @return true:如果处理了该异常信息;否则返回false
|
*/
|
private boolean handleException(Throwable ex) {
|
if (ex == null) {
|
return false;
|
}
|
// 使用Toast来显示异常信息
|
new Thread() {
|
@Override
|
public void run() {
|
Looper.prepare();
|
//T.ss("很抱歉,程序出现异常,即将退出");
|
Looper.loop();
|
}
|
}.start();
|
// 未捕获异常写文件
|
writeCrashInfoToFile(ex);
|
return true;
|
}
|
|
/**
|
* 收集程序崩溃的设备信息
|
*
|
* @param ctx
|
*/
|
private void collectCrashDeviceInfo(Context ctx) {
|
try {
|
PackageManager pm = ctx.getPackageManager();
|
|
PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(),
|
PackageManager.GET_ACTIVITIES);
|
if (pi != null) {
|
mDeviceCrashInfo.setProperty(CLIENT_VERSION,
|
pi.versionName == null ? "not set" : pi.versionName);
|
}
|
} catch (NameNotFoundException e) {
|
e.printStackTrace();
|
System.out.println("错误日志3"+e.getMessage());
|
}
|
}
|
|
/**
|
* 将异常信息写入日志文件
|
*
|
* @param ex
|
* @return
|
*/
|
private String writeCrashInfoToFile(Throwable ex) {
|
Writer info = new StringWriter();
|
PrintWriter printWriter = new PrintWriter(info);
|
// printStackTrace(PrintWriter s)
|
// 将此 throwable 及其追踪输出到指定的 PrintWriter
|
ex.printStackTrace(printWriter);
|
|
// getCause() 返回此 throwable 的 cause;如果 cause 不存在或未知,则返回 null。
|
Throwable cause = ex.getCause();
|
while (cause != null) {
|
cause.printStackTrace(printWriter);
|
cause = cause.getCause();
|
}
|
|
// toString() 以字符串的形式返回该缓冲区的当前值。
|
String result = info.toString();
|
printWriter.close();
|
|
String errorLog = String
|
.format("FINGERPRINT=%s|"
|
+ "STACK_TRACE=%s|versionName=%s|MODEL=%s|MANUFACTURER=%s|BRAND=%s|RELEASE=%s",
|
// 硬件名称
|
Build.FINGERPRINT,
|
// 异常信息
|
result,
|
// 应用版本名称
|
mDeviceCrashInfo.getProperty(CLIENT_VERSION),
|
// 设备名称
|
Build.MODEL,
|
// 硬件制造商
|
Build.MANUFACTURER,
|
// android系统定制商
|
Build.BRAND,
|
// android SDK版本
|
Build.VERSION.RELEASE);
|
//sp.setString(Constants.SP_CRASH_EXCEPTION, errorLog);
|
return null;
|
}
|
|
private void dumpExceptionToSDCard(Throwable ex) throws IOException {
|
//如果SD卡不存在或无法使用,则无法把异常信息写入SD卡
|
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
/*if (Const.DEBUG) {
|
LogUtils.w(TAG, "sdcard unmounted,skip dump exception");
|
return;
|
}*/
|
return;
|
}
|
/*File dir = new File(PATH);
|
if (!dir.exists()) {
|
dir.mkdirs();
|
}*/
|
long current = System.currentTimeMillis();
|
String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(current));
|
try {
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
PrintStream print = new PrintStream(out);
|
//导出发生异常的时间
|
print.println(time);
|
//导出手机信息
|
dumpPhoneInfo(print);
|
print.println();
|
ex.printStackTrace(print);
|
SpUtil.saveString(FILE_NAME+time,new String(out.toByteArray()));
|
print.close();
|
} catch (Exception e) {
|
e.printStackTrace();
|
System.out.println("错误日志2"+e.getMessage());
|
//LogUtils.e(TAG, "dump crash info failed");
|
}
|
//以当前时间创建log文件
|
/*File file = new File(PATH , FILE_NAME + time + FILE_NAME_SUFFIX);
|
try {
|
PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(file)));
|
//导出发生异常的时间
|
pw.println(time);
|
//导出手机信息
|
dumpPhoneInfo(pw);
|
pw.println();
|
//导出异常的调用栈信息
|
ex.printStackTrace(pw);
|
pw.close();
|
} catch (Exception e) {
|
e.printStackTrace();
|
System.out.println("错误日志2"+e.getMessage());
|
//LogUtils.e(TAG, "dump crash info failed");
|
}*/
|
}
|
private void dumpPhoneInfo(PrintStream pw) throws NameNotFoundException {
|
//应用的版本名称和版本号
|
PackageManager pm = mContext.getPackageManager();
|
PackageInfo pi = pm.getPackageInfo(mContext.getPackageName(), PackageManager.GET_ACTIVITIES);
|
pw.print("App Version: ");
|
pw.print(pi.versionName);
|
pw.print('_');
|
pw.println(pi.versionCode);
|
//android版本号
|
pw.print("OS Version: ");
|
pw.print(Build.VERSION.RELEASE);
|
pw.print("_");
|
pw.println(Build.VERSION.SDK_INT);
|
//手机制造商
|
pw.print("Vendor: ");
|
pw.println(Build.MANUFACTURER);
|
//手机型号 pw.print("Model: ");
|
pw.println(Build.MODEL);
|
//cpu架构
|
pw.print("CPU ABI: ");
|
pw.println(Build.CPU_ABI); }
|
}
|