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 类:
Application中初始化:
大致实现就是这样,主要是学习 Thread.UncaughtExceptionHandler和了解uncaughtException方法在进程Crash后会被调用。然后在这个基础上去处理逻辑(重写uncaughtException方法)。
后记
大家可以自己抛出一个异常,去测试一下是否成功。比如在Activity中使用以下代码:
成功生成的文件内容:
遇到的坑
文件创建失败,可能是文件的路径不规范
文件打开权限不足(Read Only),6.0以上需要动态权限申请
开启一个子线程去处理文件的读写,并且将当前线程sleep一定的时间。才能保证在文件写完后,程序才被杀死。
文件找不到,代码中显示已经生成,而使用DDMS查看或手机上自带的文件管理程序查看的时候却没有。可以考虑直接使用adb shell查看或者使用RE文件管理器。
参考书籍
《Android开发与艺术探索》
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 收集
- monkey自动化测试与持续集成方案--Android crash 收集
- Android应用Crash信息收集
- android 程序崩溃信息的收集【使用CrashHandler来收集应用的crash信息】
- Android开发——收集应用Crash信息到服务器
- Android 全局异常捕获之CrashHandler,用于开发者收集获取异常信息
- Android crash 收集
- Android开发中Crash异常收集与统计分析(一、Java语法相关异常)
- android异常收集错误信息并保存到crash文件夹下
- android App级别的bug信息收集(crash信息保存和发送)
- Android---Crash三部曲---收集
- Android客户端收集Crash信息的常用方法
- Android Crash日志收集
- 学习Android的一些网站收集
- android 经典文章收集
- Android平台framework层的开发(收集,ZT)
- [android UI]自定义各种UI,收集在此,以备后用!
- Android native crash log分析
- Android 网站资源收集大全