您的位置:首页 > 移动开发 > Android开发

android开发技巧-记录未捕获的crash异常日志

2016-10-09 15:39 441 查看

提出问题

开发过程中,在自测和交付测试的过程中会遇到发生crash但是无法捕获异常的情况。比如:一个人在厕所无聊瞎点,突然就crash了。你不可能带着线、电脑和手机在厕所瞎点的吧,所以你看不到电脑上crash的日志,毕竟是瞎点,那么crash的步骤你并不清楚。如果crash日志没有,那么就尴尬了。就像明明有个老王,但你却没有看清他的脸,是不是好气?

解决方案

既然有了老王,就需要在家里安装个摄像头,再来的时候,一定能记录他的外貌。而UncaughtExceptionHandler就是能够帮你记录老王面貌的工具。大多数人都把他封装为CrashHandler。打包发布的apk当然也可以将捕获的UncaughtExceptionHandler上传到公司的服务器分析。一般都选择了一种友盟分析的sdk集成来做,人家好的东西不花钱,拿来就用。

明确需求

需求很简单,就是告诉我问题出在哪里,即:where,当然能带上设备信息,即:what,和发生的时间 when会更好,因为你搞不清到底有几个老王来了几次?

方案执行

编写相关异常捕获类CrashHandler

通过实现UncaughtExceptionHandler,先通过Thread的getDefaultUncaughtExceptionHandler静态方法将原来系统提供的UncaughtExceptionHandler保存为mDefaultHandler,然后通过Thread静态方法

setDefaultUncaughtExceptionHandler设置当前Tread的处理Handler为crashHandler,当Thread有异常时回调crashHandler uncaughtException方法,先将错误日志记录在存储上,然后再通过原来系统提供的mDefaultHandler抛出异常。

public class CrashHandler implements UncaughtExceptionHandler {

/** 获取CrashHandler实例 ,单例模式 */
public static CrashHandler getInstance() {

if (crashHandler == null) {
synchronized (CrashHandler.class) {
crashHandler = new CrashHandler();
}
}
return crashHandler;

}
// 系统默认的UncaughtException处理类实例
private Thread.UncaughtExceptionHandler mDefaultHandler;
//CrashHandler静态实例
private static CrashHandler crashHandler;
// 程序的Context对象
private Context mContext;
//时间输出格式化
private SimpleDateFormat simpleDateFormat=new SimpleDateFormat("_yyyy_MM_dd_HH_mm_ss");

/** 保证只有一个CrashHandler实例 */
private CrashHandler() {

}

/**
* 初始化
*
* @param context
*/
public void init(Context context) {
mContext = context;
// 获取系统默认的UncaughtException处理器
mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
// 设置该CrashHandler为程序的默认处理器
Thread.setDefaultUncaughtExceptionHandler(this);
}

/**
* 调用Thread.setDefaultUncaughtExceptionHandler(this);后,this实现了
* UncaughtExceptionHandler,所以crash会进入到这里的回调。
* 当UncaughtException发生时会转入该重写的方法来处理
*/
@Override
public void uncaughtException(Thread thread, Throwable ex) {
if (thread == null || ex == null || mDefaultHandler == null) {
System.exit(0);
return;
}

File savePathFile = getLogFilePath(mContext);

if (savePathFile == null) {
//抛出异常
mDefaultHandler.uncaughtException(thread, ex);
return;
}

String logMessage = String
.format("CustomUncaughtExceptionHandler.uncaughtException: Thread %d Message %s",
thread.getId(), ex.getMessage());
PrintWriter printWriter = null;

try {
printWriter = new PrintWriter(new FileWriter(savePathFile, true));
logMessage = String.format("%s\r\n\r\n%s\r\n\r\nThread:
%d\r\n\r\nMessage:\r\n\r\n%s\r\n\r\nStack Trace:\r\n\r\n%s",
geDeviceInfo(mContext), new Date(),
thread.getId(), ex.getMessage(),
Log.getStackTraceString(ex));
printWriter.print(logMessage);
printWriter.print("\n\n-------------------------------------------------\n\n");
} catch (Throwable tr2) {
//最后回抛出异常,所以这里就可以不用处理了。
} finally {
if (printWriter != null) {
printWriter.close();
}
}
mDefaultHandler.uncaughtException(thread, ex);
}

/**
*
* 获取设备信息
*
* @param context
* @return String  设备信息
*/
public String geDeviceInfo(Context ctx)
{
StringBuffer deviceInfo = new StringBuffer();

deviceInfo.append(android.os.Build.MODEL).append("/");

deviceInfo.append(android.os.Build.VERSION.SDK).append("/");

deviceInfo.append(getVersionName(ctx));

return deviceInfo.toString();
}

/**
*
* 获取应用版本号
*
* @param context
* @return String  应用版本号
*/
public String getVersionName(Context context) {
String version = "";
if(context == null) {
return version;
}
try {
// 获取packagemanager的实例
PackageManager packageManager = context.getPackageManager();
// getPackageName()是你当前类的包名,0代表是获取版本信息
if(packageManager != null) {
PackageInfo packInfo;
packInfo = packageManager.getPackageInfo(context.getPackageName(),
0);
if(packInfo != null) {
version = packInfo.versionName;
}
}
} catch (Exception e) {
}
return version;
}

/**
*
* 获取日志保存路径
*
* @param context
* @return String  日志保存路径
*/
public File getLogFilePath(Context ctx) {
String sdStatus = Environment.getExternalStorageState();
if (!sdStatus.equals(Environment.MEDIA_MOUNTED)) {
return null;
}

String pathName = Environment.getExternalStorageDirectory().getPath()
+ "/Android/data/" + ctx.getPackageName()
+ "/files/error/"
+ android.os.Build.MODEL + "_"
+ getVersionName(ctx) + simpleDateFormat.format(new Date()) +".log";

File path = new File(pathName);
if (!path.getParentFile().exists()) {
path.getParentFile().mkdirs();
}

return path;
}

}


初始化CrashHandler

public class MyApplication extends Application{

@Override
public void onCreate() {

//如果是调试的apk,就初始化
if(BuildConfig.DEBUG){
CrashHandler.getInstance().init(this);
}

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: