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

Android中Crash收集

2017-04-28 20:16 239 查看
问题

Android应用不可避免会发生Crash,不管你的代码写得有多风骚,在这个复杂的网络环境中,Crash还是时常的会发生。也就是常说的应用程序发生崩溃。常见表现就是闪屏然后退出。

原因

有些Crash是只在特定网络环境中才会出现,比如说网络环境为2g的时候。而Crash是发生在用户手机端,如果我们不对这些异常信息进行收集,那我们就没办法分析出Crash的原因,也就无法进行修复。下面就介绍一下如何收集这些Crash信息。

问题分析:

首先,Crash是发生在客户端,并且带有不确定性,即有的用户可能发生而又的不发生。所以,我们可以在应用程序发生Crash后,对Crash信息进行收集,保存在本地客户端(如写入的sd卡),然后在合适的时候(如网络环境好)将Crash文件上传到服务器进行统计分析。

技术实现:

Thread中提供了一个方法叫做setDefaultUncaughtUncaughtExceptionHandler(UncaughtExceptionHandler handler)的方法,用来设置一个UncaughtExceptionHandler对象。

这个对象有什么用呢?

作用:当Crash发生的时候,系统就会回调UncaughtExceptionHandler 中的uncaughtException方法。所以我们就可以在这个方法中去保存crash日志。然后在时机好的时候发送至服务器。

具体逻辑

下面代码中,主要是创建了一个实现Thread.UncaughtExceptionHandler接口的类CrashHandler。

然后以单例的形式向外提供支持。

主要的方法有:

init (Context) 进行初始化

uncaughtException(Thread , Throwable ) 复写的方法,每次Crash之后被调用

handleException(Throwable)处理Crash的逻辑

uploadExceptionToServer()上传到服务器的实现了

dumpExceptionToSDCard(Throwable )保存到sd卡的实现类

dumpPhoneInfo(PrintWriter)获取收集信息

实现的逻辑

在Application中调用init()方法初始化CrashHandler,当发生Crash的时候CrashHandler的uncaughtException方法被调用。然后在这个方法中调用handleException方法,handleException方法中再去组织具体逻辑(调用具体实现类)。uploadExceptionToServer、dumpExceptionToSDCard、dumpPhoneInfo这三个方法则是具体逻辑实现。

代码实现:

CrashHandler 类:

public class CrashHandler implements Thread.UncaughtExceptionHandler {

private static final String TAG = "CrashHandler";
private static final boolean DEBUG = true;

private static final String PATH = Environment.getExternalStorageDirectory().getPath() + "/CrashTest/log/";

private static final String FILE_NAME = "crash";
private static final String FILE_NAME_SUFFIX = ".txt";

private static CrashHandler sInstance = new CrashHandler();

private Context mContext;

//私有构造器
private CrashHandler() {

}

//单例模式
public static CrashHandler getInstance() {

return sInstance;
}

//初始化
public void init(Context context) {

Thread.setDefaultUncaughtExceptionHandler(this);
mContext = context;

}

/**
* 重写uncaughtException
* @param t 发生Crash的线程
* @param ex Throwale对象
*/
@Override
public void uncaughtException(Thread t, Throwable ex) {
//处理逻辑需要开启一个子线程,用于文件的写入操作
handleException(ex);
//在程序关闭之前休眠2秒,以避免在文件写入的操作完
//成。之前进程被杀死。
//也可以考虑弹出对话框友好提示用户
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}

System.exit(0);
//        Process.killProcess(Process.myPid());
}

//处理异常
private void handleException(final Throwable ex) {
try {
Executors.newSingleThreadExecutor().submit(new Runnable() {
@Override
public void run() {
dumpExceptionToSDCard(ex);
uploadExceptionToServer();
}
});
} catch (Exception e) {
e.printStackTrace();
}

}

/**
* 将异常信息上传至服务器
*/
private void uploadExceptionToServer() {

}

/**
* 将异常信息写入sd卡
* @param ex
*/
private void dumpExceptionToSDCard(Throwable ex) {
//判断是否支持SD卡
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
if (DEBUG) {
Log.i(TAG, "sdcard unfind ,skip dump exception");
return;
}
}

File dir = new File(PATH);
if (!dir.exists()) {
dir.mkdirs();
}

long current = System.currentTimeMillis();
String time = new SimpleDateFormat("yyyy-MM-dd").format(new Date(current));

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) {
Log.d(TAG, "dump Exception Exception" + e.getMessage());
e.printStackTrace();
}

}

/**
*
* 获取手机信息
* @param pw 写入流
* @throws PackageManager.NameNotFoundException 异常
*/
private void dumpPhoneInfo(PrintWriter pw) throws PackageManager.NameNotFoundException {

PackageManager pm = mContext.getPackageManager();
PackageInfo pi = pm.getPackageInfo(mContext.getPackageName(), PackageManager.GET_ACTIVITIES);

pw.print("App Version : ");
pw.print(pi.versionName);
Log.d(TAG,"name : "+pi.versionName);
pw.print('_');
pw.println(pi.versionCode);

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);
pw.print("Cpu ABI : ");
pw.println(Build.CPU_ABI);
}

}


Application中初始化:

public class App extends Application {

private  Context mContext;

private static App sInstance;

@Override
public void onCreate() {
super.onCreate();
sInstance = this;

CrashHandler crashHandler = CrashHandler.getInstance();
crashHandler.init(this);

mContext = getApplicationContext();

}

public  Context getContext() {
return mContext;
}

public static App getsInstance() {
return sInstance;
}

}


大致实现就是这样,主要是学习 Thread.UncaughtExceptionHandler和了解uncaughtException方法在进程Crash后会被调用。然后在这个基础上去处理逻辑(重写uncaughtException方法)。

后记

大家可以自己抛出一个异常,去测试一下是否成功。比如在Activity中使用以下代码:

throw new RuntimeException("自定义异常");


成功生成的文件内容:



遇到的坑

文件创建失败,可能是文件的路径不规范

文件打开权限不足(Read Only),6.0以上需要动态权限申请

开启一个子线程去处理文件的读写,并且将当前线程sleep一定的时间。才能保证在文件写完后,程序才被杀死。

文件找不到,代码中显示已经生成,而使用DDMS查看或手机上自带的文件管理程序查看的时候却没有。可以考虑直接使用adb shell查看或者使用RE文件管理器。

参考书籍

《Android开发与艺术探索》
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android Crash