好久之前记录到word中的,也忘记参考的哪篇博客……真的不记得了
工程目录
概述
Thread的run方法是不抛出任何检查型异常(checked exception)的,但是它自身却可能因为一个异常而被终止,导致这个线程的终结
JDK5.0之前,不能为单独的Thread设置UncaughtExceptionHandler,也不能指定一个默认的UncaughtExceptionHandler。为了可以设置一个UncaughtExceptionHandler,需要去继承ThreadGroup并覆写uncaughtException方法
JDK5.0及之后,我们通过Thread的实例方法setUncaughtExceptionHandler,可以为任何一个Thread设置一个UncaughtExceptionHandler。当然你也可以为所有Thread设置一个默认的UncaughtExceptionHandler,通过调用Thread.setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)方法,这是Thread的一个static方法
主要文件
CrashHandler.java
public class CrashHandler implements Thread.UncaughtExceptionHandler {
public static final String TAG = "CrashHandler";
private static CrashHandler sInstance = new CrashHandler();// CrashHandler 实例
private Context mContext;// 程序的 Context 对象
private Thread.UncaughtExceptionHandler mDefaultHandler;// 系统默认的 UncaughtException 处理类
private DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");// 用于格式化日期,作为日志文件名的一部分
private String pathLog = "/sdcard/crash/";//log存储的路径
private Map<String, String> infos = new HashMap<String, String>();// 用来存储设备信息和异常信息
/**
* 保证只有一个 CrashHandler 实例
*/
private CrashHandler() {
}
/**
* 获取 CrashHandler 实例 ,单例模式
*/
public static CrashHandler getInstance() {
return sInstance;
}
/**
* 初始化
* @param context
*/
public void init(Context context) {
mContext = context;
mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();// 获取系统默认的 UncaughtException 处理器
Thread.setDefaultUncaughtExceptionHandler(this);// 设置该 CrashHandler 为程序的默认处理器
}
/**
* 当 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);
// 重新启动程序,注释上面的退出程序
/*Intent intent = new Intent();
intent.setClass(mContext, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent);
android.os.Process.killProcess(android.os.Process.myPid());*/
}
}
/**
* 自定义错误处理,收集错误信息,发送错误报告等操作均在此完成
* @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(mContext, "很抱歉,程序出现异常,即将退出。", Toast.LENGTH_LONG).show();
Looper.loop();
}
}.start();
collectDeviceInfo(mContext);// 收集设备参数信息
saveCrashInfo2File(ex);// 保存日志文件
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 (PackageManager.NameNotFoundException e) {
Log.e(TAG, "an error occured when collect package info", e);
}
Field[] fields = Build.class.getDeclaredFields();
for (Field field : fields) {
try {
field.setAccessible(true);
infos.put(field.getName(), field.get(null).toString());
Log.d(TAG, field.getName() + " : " + field.get(null));
} catch (Exception e) {
Log.e(TAG, "an error occured when collect crash info", e);
}
}
System.out.println(infos.toString());
}
/**
* 保存错误信息到文件中
* @param ex
* @return 返回文件名称, 便于将文件传送到服务器
*/
private String saveCrashInfo2File(Throwable ex) {
StringBuffer sb = new StringBuffer();
for (Map.Entry<String, String> 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.close();
String result = writer.toString();
System.out.println("CrashHandler.saveCrashInfo2File"+result);
sb.append(result);
try {
long timestamp = System.currentTimeMillis();
String time = formatter.format(new Date());
String fileName = "crash-" + time + "-" + timestamp + ".log";
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
File dir = new File(pathLog);
if (!dir.exists()) {
dir.mkdirs();
}
FileOutputStream fos = new FileOutputStream(pathLog + fileName);
fos.write(sb.toString().getBytes());
fos.close();
}
return fileName;
} catch (Exception e) {
Log.e(TAG, "an error occured while writing file...", e);
}
return null;
}
}
CrashApplication .java
public class CrashApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
CrashHandler crashHandler = CrashHandler.getInstance();
crashHandler.init(getApplicationContext());
}
}
AndroidManifest.xml
<application
android:label="@string/app_name"
android:name=".CrashApplication"
android:icon="@drawable/ic_launcher">
<activity
android:name="MainActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".ExceptionActivity"/>
</application>
运行效果
输出的设备及异常信息:
SUPPORTED_64_BIT_ABIS=[Ljava.lang.String;@b416fd0
versionCode=1
BOARD=BalongV8R1SFT
BOOTLOADER=unknown
TYPE=user
matchers=[Ljava.lang.String;@309612ce
ID=HuaweiALE-UL00
TIME=1450304496000
BRAND=Huawei
TAG=Build
SERIAL=N2F4C15905016654
HARDWARE=hi6210sft
SUPPORTED_ABIS=[Ljava.lang.String;@5ce2bc9
NO_HOTA=false
CPU_ABI=arm64-v8a
IS_DEBUGGABLE=false
RADIO=unknown
replacements=[Ljava.lang.String;@2f4f5ef
MANUFACTURER=HUAWEI
SUPPORTED_32_BIT_ABIS=[Ljava.lang.String;@187d7082
TAGS=release-keys
CPU_ABI2=
UNKNOWN=unknown
USER=android
FINGERPRINT=Huawei/ALE-UL00/hwALE-H:5.0.2/HuaweiALE-UL00/C00B230:user/release-keys
HOST=localhost#1
PRODUCT=ALE-UL00
versionName=1.0
DISPLAY=ALE-UL00C00B230
HIDE_PRODUCT_INFO=false
MODEL=ALE-UL00
DEVICE=hwALE-H
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.luomo.gexception/com.luomo.gexception.ExceptionActivity}: java.lang.ArithmeticException: divide by zero
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2432)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2492)
at android.app.ActivityThread.access$1200(ActivityThread.java:158)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1349)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5564)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:960)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)
Caused by: java.lang.ArithmeticException: divide by zero
at com.luomo.gexception.ExceptionActivity.onCreate(ExceptionActivity.java:10)
at android.app.Activity.performCreate(Activity.java:6013)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1108)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2385)
... 10 more
java.lang.ArithmeticException: divide by zero
at com.luomo.gexception.ExceptionActivity.onCreate(ExceptionActivity.java:10)
at android.app.Activity.performCreate(Activity.java:6013)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1108)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2385)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2492)
at android.app.ActivityThread.access$1200(ActivityThread.java:158)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1349)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5564)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:960)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)