做Android开发应用上线的时候,需要测试客户端稳定性,所以我们需要提前发现问题,收集更多异常信息,当Android客户端发生异常的时候,如何捕获异常信息呢,这里我们通过重写一个类,实现Thread.UncaughtExceptionHandler接口,重写uncaughtException方法,在uncaughtException方法中,可以把异常存到本地或上传服务端。
由于异常大多发生在在多线程环境中,子线程抛出的异常是不能用主线程中try….catch捕获。可以给线程设置UncaughtExceptionHandler,当出现异常时会回调UncaughtExceptionHandler中的uncaughtException(Thread t, Throwable e) 方法。设置可以为某个线程单独
thread.setUncaughtExceptionHandler(new DefalutUncaughtExceptionHandler());
当想给所有线程设置时使用:
thread.setDefaultUncaughtExceptionHandler(new DefalutUncaughtExceptionHandler());
t实现 UncaughtExceptionHandler中的 uncaughtException方法aughtEx
ceptionHandler(new DefalutUncaughtExceptionHandler())
@Override
public void uncaughtException(final Thread t, final Throwable e) {
重写此方法做相关操作
}
自己实现一个异常检查的类,根据抛出的异常,将异常信息保存在sd卡中,并且提供了文件头部信息,显示应该版本号,机型,系统版本等相关属性:
package org.ss.ssdemo.utils;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Environment;
import android.support.annotation.NonNull;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.Thread.UncaughtExceptionHandler;
import java.text.Format;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @ClassName CrashTool
* @Description 异常工具类
*/
public class CrashTool {
private static final String FILE_SEP = System.getProperty("file.separator");
//----------------------------------------------------------------------------------------------
private static final Format FORMAT = new SimpleDateFormat("MM-dd HH-mm-ss", Locale.getDefault());
private static final String CRASH_HEAD;
private static final UncaughtExceptionHandler DEFAULT_UNCAUGHT_EXCEPTION_HANDLER;
private static final UncaughtExceptionHandler UNCAUGHT_EXCEPTION_HANDLER;
private static Context mContext;
private static String mCrashDirPath;
private static String dir;
private static String versionName;
private static int versionCode;
private static ExecutorService sExecutor;
//显示异常日志的头部信息,比如设备名称,设备型号,系统版本,SDK版本
static {
CRASH_HEAD = "\n************* Crash Log Head ****************" +
"\nDevice Manufacturer: " + Build.MANUFACTURER +// 设备厂商
"\nDevice Model : " + Build.MODEL +// 设备型号
"\nAndroid Version : " + Build.VERSION.RELEASE +// 系统版本
"\nAndroid SDK : " + Build.VERSION.SDK_INT +// SDK版本
"\nApp VersionName : " + versionName +
"\nApp VersionCode : " + versionCode +
"\n************* Crash Log Head ****************\n\n";
DEFAULT_UNCAUGHT_EXCEPTION_HANDLER = Thread.getDefaultUncaughtExceptionHandler();
UNCAUGHT_EXCEPTION_HANDLER = new UncaughtExceptionHandler() {
@Override
public void uncaughtException(final Thread t, final Throwable e) {
//从操作系统中结束掉当前程序的进程
if (e == null) {
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(0);
return;
}
//根据时间生成一个文本,进行异常日志写入
Date now = new Date(System.currentTimeMillis());
String fileName = FORMAT.format(now) + ".txt";
final String fullPath = (dir == null ? mCrashDirPath : dir) + fileName;
if (!createOrExistsFile(fullPath)) {
return;
}
if (sExecutor == null) {
sExecutor = Executors.newSingleThreadExecutor();
}
sExecutor.execute(new Runnable() {
@Override
public void run() {
PrintWriter pw = null;
try {
pw = new PrintWriter(new FileWriter(fullPath, false));
pw.write(CRASH_HEAD);
e.printStackTrace(pw);
Throwable cause = e.getCause();
while (cause != null) {
cause.printStackTrace(pw);
cause = cause.getCause();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (pw != null) {
pw.close();
}
}
}
});
if (DEFAULT_UNCAUGHT_EXCEPTION_HANDLER != null) {
DEFAULT_UNCAUGHT_EXCEPTION_HANDLER.uncaughtException(t, e);
}
}
};
}
/**
* 初始化
* <p>需添加权限 {@code <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>}</p>
*/
public static void init(Context context) {
mContext = context;
try {
PackageInfo pi = mContext.getPackageManager().getPackageInfo(mContext.getPackageName(), 0);
if (pi != null) {
versionName = pi.versionName;
versionCode = pi.versionCode;
}
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
init("");
}
/**
* 初始化
* <p>需添加权限 {@code <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>}</p>
*
* @param crashDir 崩溃文件存储目录
*/
public static void init(@NonNull final File crashDir) {
init(crashDir.getAbsolutePath());
}
/**
* 初始化
* <p>需添加权限 {@code <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>}</p>
*
* @param crashDir 崩溃文件存储目录
*/
public static void init(final String crashDir) {
versionName = getAppVersionName(mContext);
versionCode = getAppVersionNo(mContext);
if (isSpace(crashDir)) {
dir = null;
} else {
dir = crashDir.endsWith(FILE_SEP) ? crashDir : crashDir + FILE_SEP;
}
try {
mCrashDirPath = getRootPath() + File.separator + "ssdemo" + File.separator + "crash" + File.separator;
} catch (Exception e) {
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
mCrashDirPath = mContext.getExternalCacheDir().getPath() + File.separator + "crash" + File.separator;
} else {
mCrashDirPath = mContext.getCacheDir().getPath() + File.separator + "crash" + File.separator;
}
}
Thread.setDefaultUncaughtExceptionHandler(UNCAUGHT_EXCEPTION_HANDLER);
}
private static boolean createOrExistsFile(final String filePath) {
File file = new File(filePath);
if (file.exists()) {
return file.isFile();
}
if (!createOrExistsDir(file.getParentFile())) {
return false;
}
try {
return file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
private static boolean createOrExistsDir(final File file) {
return file != null && (file.exists() ? file.isDirectory() : file.mkdirs());
}
private static boolean isSpace(final String s) {
if (s == null) {
return true;
}
for (int i = 0, len = s.length(); i < len; ++i) {
if (!Character.isWhitespace(s.charAt(i))) {
return false;
}
}
return true;
}
public static File getRootPath() {
File path = null;
if (sdCardIsAvailable()) {
path = Environment.getExternalStorageDirectory();
} else {
path = Environment.getDataDirectory();
}
return path;
}
public static boolean sdCardIsAvailable() {
if (Environment.getExternalStorageState().equals("mounted")) {
File sd = new File(Environment.getExternalStorageDirectory().getPath());
return sd.canWrite();
} else {
return false;
}
}
/****
* @Method getAppVersionName
* @Description 获取当前应用版本名称
* ***/
public static String getAppVersionName(Context context) {
PackageManager packageManager = context.getPackageManager();
PackageInfo packInfo = null;
try {
packInfo = packageManager.getPackageInfo(context.getPackageName(), 0);
} catch (PackageManager.NameNotFoundException var4) {
var4.printStackTrace();
}
String version = packInfo.versionName;
return version;
}
/****
* @Method getAppVersionNo
* @Description 获取当前应用版本号
* ***/
public static int getAppVersionNo(Context context) {
PackageManager packageManager = context.getPackageManager();
PackageInfo packInfo = null;
try {
packInfo = packageManager.getPackageInfo(context.getPackageName(), 0);
} catch (PackageManager.NameNotFoundException var4) {
var4.printStackTrace();
}
int version = packInfo.versionCode;
return version;
}
}