UncaughtExceptionHandler接口实现

首先创建一个OPhone项目(项目的创建请参考OPhoneSDN上的其他文章),本文示例项目名称为:CrashReporter ;包名为:org.goodev.cr;并创建一个默认的Activity名字为:ReporterTest。然后创建CrashHandler类实现UncaughtExceptionHandler接口,并实现其函数:public void uncaughtException(Thread thread, Throwable ex)。CrashHandler类实现了错误报告的主要处理逻辑,该类代码如下(在代码中会有详细注释来解释各种处理情况):

packageorg.goodev.cr;

Import 省略...;

/**

* UncaughtException处理类,当程序发生Uncaught异常的时候,有该类

* 来接管程序,并记录 发送错误报告.

*

*/

publicclassCrashHandlerimplementsUncaughtExceptionHandler {

/** Debug Log tag*/

publicstaticfinalString TAG ="CrashHandler";

/** 是否开启日志输出,在Debug状态下开启,

* 在Release状态下关闭以提示程序性能

* */

publicstaticfinalbooleanDEBUG =true;

/** 系统默认的UncaughtException处理类 */

privateThread.UncaughtExceptionHandler mDefaultHandler;

/** CrashHandler实例 */

privatestaticCrashHandler INSTANCE;

/** 程序的Context对象 */

privateContext mContext;

/** 使用Properties来保存设备的信息和错误堆栈信息*/

privateProperties mDeviceCrashInfo =newProperties();

privatestaticfinalString VERSION_NAME ="versionName";

privatestaticfinalString VERSION_CODE ="versionCode";

privatestaticfinalString STACK_TRACE ="STACK_TRACE";

/** 错误报告文件的扩展名 */

privatestaticfinalString CRASH_REPORTER_EXTENSION =".cr";

/** 保证只有一个CrashHandler实例 */

privateCrashHandler() {}

/** 获取CrashHandler实例 ,单例模式*/

publicstaticCrashHandler getInstance() {

if(INSTANCE ==null) {

INSTANCE =newCrashHandler();

}

returnINSTANCE;

}

/**

* 初始化,注册Context对象,

* 获取系统默认的UncaughtException处理器,

* 设置该CrashHandler为程序的默认处理器

*

* @param ctx

*/

publicvoidinit(Context ctx) {

mContext = ctx;

mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();

Thread.setDefaultUncaughtExceptionHandler(this);

}

/**

* 当UncaughtException发生时会转入该函数来处理

*/

@Override

publicvoiduncaughtException(Thread thread, Throwable ex) {

if(!handleException(ex) && mDefaultHandler !=null) {

//如果用户没有处理则让系统默认的异常处理器来处理

mDefaultHandler.uncaughtException(thread, ex);

}else{

//Sleep一会后结束程序

try{

Thread.sleep(3000);

}catch(InterruptedException e) {

Log.e(TAG,"Error : ", e);

}

Android.os.Process.killProcess(android.os.Process.myPid());

System.exit(10);

}

}

/**

* 自定义错误处理,收集错误信息

* 发送错误报告等操作均在此完成.

* 开发者可以根据自己的情况来自定义异常处理逻辑

* @param ex

* @return true:如果处理了该异常信息;否则返回false

*/

privatebooleanhandleException(Throwable ex) {

if(ex ==null) {

returntrue;

}

finalString msg = ex.getLocalizedMessage();

//使用Toast来显示异常信息

newThread() {

@Override

publicvoidrun() {

Looper.prepare();

Toast.makeText(mContext,"程序出错啦:"+ msg, Toast.LENGTH_LONG)

.show();

Looper.loop();

}

}.start();

//收集设备信息

collectCrashDeviceInfo(mContext);

//保存错误报告文件

String crashFileName = saveCrashInfoToFile(ex);

//发送错误报告到服务器

sendCrashReportsToServer(mContext);

returntrue;

}

/**

* 在程序启动时候, 可以调用该函数来发送以前没有发送的报告

*/

publicvoidsendPreviousReportsToServer() {

sendCrashReportsToServer(mContext);

}

/**

* 把错误报告发送给服务器,包含新产生的和以前没发送的.

*

* @param ctx

*/

privatevoidsendCrashReportsToServer(Context ctx) {

String[] crFiles = getCrashReportFiles(ctx);

if(crFiles !=null&& crFiles.length >0) {

TreeSet sortedFiles =newTreeSet();

sortedFiles.addAll(Arrays.asList(crFiles));

for(String fileName : sortedFiles) {

File cr =newFile(ctx.getFilesDir(), fileName);

postReport(cr);

cr.delete();// 删除已发送的报告

}

}

}

privatevoidpostReport(File file) {

// TODO 使用HTTP Post 发送错误报告到服务器

// 这里不再详述,开发者可以根据OPhoneSDN上的其他网络操作

// 教程来提交错误报告

}

/**

* 获取错误报告文件名

* @param ctx

* @return

*/

privateString[] getCrashReportFiles(Context ctx) {

File filesDir = ctx.getFilesDir();

FilenameFilter filter =newFilenameFilter() {

publicbooleanaccept(File dir, String name) {

returnname.endsWith(CRASH_REPORTER_EXTENSION);

}

};

returnfilesDir.list(filter);

}

/**

* 保存错误信息到文件中

* @param ex

* @return

*/

privateString saveCrashInfoToFile(Throwable ex) {

Writer info =newStringWriter();

PrintWriter printWriter =newPrintWriter(info);

ex.printStackTrace(printWriter);

Throwable cause = ex.getCause();

while(cause !=null) {

cause.printStackTrace(printWriter);

cause = cause.getCause();

}

String result = info.toString();

printWriter.close();

mDeviceCrashInfo.put(STACK_TRACE, result);

try{

longtimestamp = System.currentTimeMillis();

String fileName ="crash-"+ timestamp + CRASH_REPORTER_EXTENSION;

FileOutputStream trace = mContext.openFileOutput(fileName,

Context.MODE_PRIVATE);

mDeviceCrashInfo.store(trace,"");

trace.flush();

trace.close();

returnfileName;

}catch(Exception e) {

Log.e(TAG,"an error occured while writing report file...", e);

}

returnnull;

}

/**

* 收集程序崩溃的设备信息

*

* @param ctx

*/

publicvoidcollectCrashDeviceInfo(Context ctx) {

try{

PackageManager pm = ctx.getPackageManager();

PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(),

PackageManager.GET_ACTIVITIES);

if(pi !=null) {

mDeviceCrashInfo.put(VERSION_NAME,

pi.versionName ==null?"not set": pi.versionName);

mDeviceCrashInfo.put(VERSION_CODE, pi.versionCode);

}

}catch(NameNotFoundException e) {

Log.e(TAG,"Error while collect package info", e);

}

//使用反射来收集设备信息.在Build类中包含各种设备信息,

//例如: 系统版本号,设备生产商 等帮助调试程序的有用信息

//具体信息请参考后面的截图

Field[] fields = Build.class.getDeclaredFields();

for(Field field : fields) {

try{

field.setAccessible(true);

mDeviceCrashInfo.put(field.getName(), field.get(null));

if(DEBUG) {

Log.d(TAG, field.getName() +" : "+ field.get(null));

}

}catch(Exception e) {

Log.e(TAG,"Error while collect crash info", e);

}

}

}

}

在上面CrashHandler实现中,当错误发生的时候使用Toast显示错误信息,然后收集错误报告并保存在文件中。 发送错误报告代码请读者自己实现。在uncaughtException函数中调用了Thread.sleep(3000);来让线程停止一会是为了显示Toast信息给用户,然后Kill程序。如果你不用Toast来显示信息则可以去除该代码。除了Toast外,开发者还可以选择使用Notification来显示错误内容并让用户选择是否提交错误报告而不是自动提交。关于Notification的实现请读者参考:NotificationManager。在发送错误报道的时候,可以先检测网络是否可用,如果不可用则可以在以后网络情况可用的情况下发送。 网络监测代码如下:

/**

* 检测网络连接��否可用

* @param ctx

* @return true 可用; false 不可用

*/

privatebooleanisNetworkAvailable(Context ctx) {

ConnectivityManager cm =

(ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE);

if(cm ==null) {

returnfalse;

}

NetworkInfo[] netinfo = cm.getAllNetworkInfo();

if(netinfo ==null) {

returnfalse;

}

for(inti =0; i

if(netinfo[i].isConnected()) {

returntrue;

}

}

returnfalse;

}

下面是在模拟器和笔者手机(Dell mini3i OPhone1.5系统)上收集的具体信息截图:

图二: 模拟器信息截图

图三:Dell mini3i 设备信息截图

Application 实现

实现一个自定义Application来注册CrashHandler. 代码如下:

publicclassCrashApplicationextendsApplication {

@Override

publicvoidonCreate() {

super.onCreate();

CrashHandler crashHandler = CrashHandler.getInstance();

//注册crashHandler

crashHandler.init(getApplicationContext());

//发送以前没发送的报告(可选)

crashHandler.sendPreviousReportsToServer();

}

}

在AndroidManifest.xml中注册

最后只要在AndroidManifest.xml中注册CrashApplication就可以了。代码如下:

package="org.goodev.cr"

android:versionCode="1"

android:versionName="1.0">

android:name=".CrashApplication">

android:label="@string/app_name">

总结:通过本文示例的方式,开发者可以在程序中收集详细的崩溃信息,从而为调试程序带来便利,如果您的程序还没有该功能赶快加入吧。crashReporter.zip中包含本文使用的项目文件及资源。

android 统一错误处理,Android 程序错误处理全局处理相关推荐

  1. Photoshop储存为psd出现程序错误提示怎么办?程序错误解决教程

    Photoshop是大家进行平面设计是不可或缺的图像处理工具,然而大家在使用Photoshop做图像处理,准备将psd文件保存到电脑,是不是经常遇到过弹出了Photoshop[无法完成请求,因为程序错 ...

  2. C语言关闭文件总是错误,C语言程序错误,不能正常读写文件,求解啊

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 #define LEN sizeof(struct student) #include #include #include struct student ...

  3. c语言程序出现错误,C语言程序错误,不能正常读写文件,求解啊

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 #define LEN sizeof(struct student) #include #include #include struct student ...

  4. 【错误记录】记录 Android 命令行执行 Java 程序中出现的错误 ( dx 打包 PC 可执行文件报错 | dalvik 命令执行 kotlin 编译的 dex 文件报错 )

    文章目录 前言 一.Android 命令行与 PC 可执行 JAR 文件不兼容 二.Android 命令行使用 dalvik 命令不能直接执行 Kotlin 编译的 dex 文件 前言 尝试在 And ...

  5. 【Android 应用开发】Android 开发错误集锦

    1. eclipse的Device中不显示手机 在eclipse中连接不上手机,出现adb server didn't ACK  fail to start daemon 错误. 出现这种原因是因为a ...

  6. 基于ubuntu16.04多用户编译android N(android 7.1)系统提示ninja_wrapper错误问题

    基于ubuntu16.04多用户编译android N(android 7.1)系统提示ninja_wrapper错误问题 Ubuntu 1604系统除了root,还有kandi和sundi两个用户, ...

  7. Android运行Socket项目时出现错误 Error: ShouldNotReachHere()

    在Android项目中实现Socket通信,服务器端使用main方法创建ServerSocket,运行启动服务器时报错"Error: ShouldNotReachHere() ". ...

  8. Android日志[进阶篇]五-阅读错误报告

    Android日志[进阶篇]一-使用 Logcat 写入和查看日志 Android日志[进阶篇]二-分析堆栈轨迹(调试和外部堆栈) Android日志[进阶篇]三-Logcat命令行工具 Androi ...

  9. android studio抛出,Android Studio中新的项目不能运行,抛出错误(Android Studio new pr

    刚才我已经安装,并开始在Android Studio中工作,并配置我需要开始上项目的工作一切之后创建的新项目. 在新创建的项目,我没有改变任何东西,然后试图运行项目. 但它不工作,抛出一些相关性错误, ...

最新文章

  1. 如何启动linux上的svn服务
  2. JBOSS的管理员账号和密码设定
  3. java 怎么自定义排序_Java如何实现List自定义排序
  4. 首届 KubeCon 2020 线上峰会隆重举办 | 云原生生态周报 Vol. 59
  5. 01.elasticsearch-mapping全面解析
  6. jekenis父子结构项目打包_自动打包机的型号怎么选择?自动打包机型号选择须知...
  7. Linux C 指针练习
  8. emacs中安装markdown-mode
  9. 台达asda-b2伺服驱动器说明书_台达解决方案提升粉末冶金液压机的控制精度
  10. mybatis SqlMapConfig.xml
  11. nimm博弈必胜方可操作种数HDU - 1850
  12. 用JSF实现页面刷新后,checkbox仍处于选中状态
  13. LaTex字体、符号汇总
  14. 回文数 详解(C++)
  15. IDL处理葵花8Himawari-8标准HSD数据——制作大气校正数据集(卫星角度数据)
  16. 无法更改硬件兼容性时解决“虚拟机使用的是此版本 VMware Workstation 不支持的硬件版本”的方法
  17. 2019CBA全明星周末举行正赛 南方明星队获胜
  18. python12岁该学吗_本人12岁,对编程感兴趣,之前也学过python,被爸妈打消积极性,面对爸妈的反对,我该顺从还是继续?...
  19. lstm实战,nlp情感分析(Kaggle)
  20. C语言的s8数据结构

热门文章

  1. JDK源码(15)-Class
  2. 程序员的算法课(18)-常用的图算法:广度优先(BFS)
  3. s:property=a value=/取的s:debug/s:debug中的value stack中的属性值
  4. lua urlencode urldecode URL编码
  5. Atitit.输入法配置说明v1 q229
  6. 关闭 IOS8 最近使用 最近联系人
  7. Entity Framework 4 in Action 读书笔记——开篇
  8. [ZZ]关于内存中栈和堆的区别
  9. 字符串、列表、元组、字典
  10. 计算机网络课设telnet_【川大】计算机网络课程设计9013,奥鹏2017