首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 移动开发 > Android >

在Android中自定义捕获Application全局错误,可以替换掉系统的强制退出对话框(很有参考价值与实用价值)

2013-12-10 
在Android中自定义捕获Application全局异常,可以替换掉系统的强制退出对话框(很有参考价值与实用价值)?在

在Android中自定义捕获Application全局异常,可以替换掉系统的强制退出对话框(很有参考价值与实用价值)

?

在 MainActivity.java 代码中,代码是这样写的:

?

[java]?view plaincopy
  1. package?com.scott.crash;??
  2. ??
  3. import?android.app.Activity;??
  4. import?android.os.Bundle;??
  5. ??
  6. public?class?MainActivity?extends?Activity?{??
  7. ??
  8. ????private?String?s;??
  9. ??
  10. ????@Override??
  11. ????public?void?onCreate(Bundle?savedInstanceState)?{??
  12. ????????super.onCreate(savedInstanceState);??
  13. ????????System.out.println(s.equals("any?string"));??
  14. ????}??
  15. ??
  16. }??

?

我们在这里故意制造了一个潜在的运行期异常,当我们运行程序时就会出现以下界面:

在Android中自定义捕获Application全局错误,可以替换掉系统的强制退出对话框(很有参考价值与实用价值)

?

遇到软件没有捕获的异常之后,系统会弹出这个默认的强制关闭对话框。

我们当然不希望用户看到这种现象,简直是对用户心灵上的打击,而且对我们的 BUG 的修复也是毫无帮助的。我们需要的是软件有一个全局的异常捕获器,当出现一个我们没有发现的异常时,捕获这个异常,并且将异常信息记录下来,上传到服务器公开发这分析出现异常的具体原因。

?

接下来我们就来实现这一机制,不过首先我们还是来了解以下两个类:android.app.Application 和java.lang.Thread.UncaughtExceptionHandler。

? ??1、Application:用来管理应用程序的全局状态。在应用程序启动时 Application 会首先创建,然后才会根据情况(Intent)来启动相应的Activity 和 Service。本示例中将在自定义加强版的 Application 中注册未捕获异常处理器。

? ??2、Thread.UncaughtExceptionHandler:线程未捕获异常处理器,用来处理未捕获异常。如果程序出现了未捕获异常,默认会弹出系统中强制关闭对话框。我们需要实现此接口,并注册为程序中默认未捕获异常处理。这样当未捕获异常发生时,就可以做一些个性化的异常处理操作。

?

大家在刚才的项目结构图中看到的 CrashHandler.java 实现了 Thread.UncaughtExceptionHandler,使我们用来处理未捕获异常的主要成员,代码如下:

?

[java]?view plaincopy
  1. package?com.scott.crash;????
  2. ????
  3. import?java.io.File;????
  4. import?java.io.FileOutputStream;????
  5. import?java.io.PrintWriter;????
  6. import?java.io.StringWriter;????
  7. import?java.io.Writer;????
  8. import?java.lang.Thread.UncaughtExceptionHandler;????
  9. import?java.lang.reflect.Field;????
  10. import?java.text.DateFormat;????
  11. import?java.text.SimpleDateFormat;????
  12. import?java.util.Date;????
  13. import?java.util.HashMap;????
  14. import?java.util.Map;????
  15. ????
  16. import?android.content.Context;????
  17. import?android.content.pm.PackageInfo;????
  18. import?android.content.pm.PackageManager;????
  19. import?android.content.pm.PackageManager.NameNotFoundException;????
  20. import?android.os.Build;????
  21. import?android.os.Environment;????
  22. import?android.os.Looper;????
  23. import?android.util.Log;????
  24. import?android.widget.Toast;??
  25. ??
  26. /**??
  27. ?*?UncaughtException处理类,当程序发生Uncaught异常的时候,有该类来接管程序,并记录发送错误报告.??
  28. ?*???
  29. ?*?@author?user??
  30. ?*???
  31. ?*/??
  32. public?class?CrashHandler?implements?UncaughtExceptionHandler?{??
  33. ??
  34. ????public?static?final?String?TAG?=?"CrashHandler";??
  35. ??
  36. ????//?CrashHandler?实例??
  37. ????private?static?CrashHandler?INSTANCE?=?new?CrashHandler();??
  38. ??
  39. ????//?程序的?Context?对象??
  40. ????private?Context?mContext;??
  41. ??
  42. ????//?系统默认的?UncaughtException?处理类??
  43. ????private?Thread.UncaughtExceptionHandler?mDefaultHandler;??
  44. ??
  45. ????//?用来存储设备信息和异常信息??
  46. ????private?Map<String,?String>?infos?=?new?HashMap<String,?String>();??
  47. ??
  48. ????//?用于格式化日期,作为日志文件名的一部分??
  49. ????private?DateFormat?formatter?=?new?SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");??
  50. ??
  51. ??
  52. ????/**?保证只有一个?CrashHandler?实例?*/??
  53. ????private?CrashHandler()?{??
  54. ????}??
  55. ??
  56. ????/**?获取?CrashHandler?实例?,单例模式?*/??
  57. ????public?static?CrashHandler?getInstance()?{??
  58. ????????return?INSTANCE;??
  59. ????}??
  60. ??
  61. ????/**?
  62. ?????*?初始化?
  63. ?????*?
  64. ?????*?@param?context?
  65. ?????*/??
  66. ????public?void?init(Context?context)?{??
  67. ????????mContext?=?context;??
  68. ??????
  69. ????????//?获取系统默认的?UncaughtException?处理器??
  70. ????????mDefaultHandler?=?Thread.getDefaultUncaughtExceptionHandler();??
  71. ??????
  72. ????????//?设置该?CrashHandler?为程序的默认处理器??
  73. ????????Thread.setDefaultUncaughtExceptionHandler(this);??
  74. ????}??
  75. ??
  76. ????/**?
  77. ?????*?当?UncaughtException?发生时会转入该函数来处理?
  78. ?????*/??
  79. ????@Override??
  80. ????public?void?uncaughtException(Thread?thread,?Throwable?ex)?{??
  81. ????????if?(!handleException(ex)?&&?mDefaultHandler?!=?null)?{??
  82. ????????????//?如果用户没有处理则让系统默认的异常处理器来处理??
  83. ????????????mDefaultHandler.uncaughtException(thread,?ex);??
  84. ????????}?else?{??
  85. ????????????try?{??
  86. ????????????????Thread.sleep(3000);??
  87. ????????????}?catch?(InterruptedException?e)?{??
  88. ????????????????Log.e(TAG,?"error?:?",?e);??
  89. ????????????}??
  90. ??
  91. ????????????//?退出程序,注释下面的重启启动程序代码
  92. ????????????android.os.Process.killProcess(android.os.Process.myPid());??
  93. ????????????System.exit(1);??
  94.    ? ? ?
  95.    ? ? ?// 重新启动程序,注释上面的退出程序
  96. ? ? ? ? ? ?Intent intent = new Intent();
  97. ? ? ? ? ? ?intent.setClass(mContext,MainActivity.class);
  98. ? ? ? ? ? ?intent.addFlag(Intent.FLAG_ACTIVITY_NEW_TASK);
  99. ? ? ? ? ? ?mContext.startActivity(intent);
  100. ? ? ? ? ? ?android.os.Process.killProcess(android.os.Process.myPid());
  101. ? ? ? ? }??
  102. ????}??
  103. ??
  104. ????/**?
  105. ?????*?自定义错误处理,收集错误信息,发送错误报告等操作均在此完成?
  106. ?????*??
  107. ?????*?@param?ex?
  108. ?????*?@return?true:如果处理了该异常信息;否则返回?false?
  109. ?????*/??
  110. ????private?boolean?handleException(Throwable?ex)?{??
  111. ????????if?(ex?==?null)?{??
  112. ????????????return?false;??
  113. ????????}??
  114. ??
  115. ????????//?使用?Toast?来显示异常信息??
  116. ????????new?Thread()?{??
  117. ????????????@Override??
  118. ????????????public?void?run()?{??
  119. ????????????????Looper.prepare();??
  120. ????????????????Toast.makeText(mContext,?"很抱歉,程序出现异常,即将退出。",?Toast.LENGTH_LONG).show();??
  121. ????????????????Looper.loop();??
  122. ????????????}??
  123. ????????}.start();??
  124. ??
  125. ????????//?收集设备参数信息??
  126. ????????collectDeviceInfo(mContext);??
  127. ????????//?保存日志文件??
  128. ????????saveCrashInfo2File(ex);??
  129. ????????return?true;??
  130. ????}??
  131. ??
  132. ????/**?
  133. ?????*?收集设备参数信息?
  134. ?????*?@param?ctx?
  135. ?????*/??
  136. ????public?void?collectDeviceInfo(Context?ctx)?{??
  137. ????????try?{??
  138. ????????????PackageManager?pm?=?ctx.getPackageManager();??
  139. ????????????PackageInfo?pi?=?pm.getPackageInfo(ctx.getPackageName(),?PackageManager.GET_ACTIVITIES);??
  140. ??
  141. ????????????if?(pi?!=?null)?{??
  142. ????????????????String?versionName?=?pi.versionName?==?null???"null"?:?pi.versionName;??
  143. ????????????????String?versionCode?=?pi.versionCode?+?"";??
  144. ????????????????infos.put("versionName",?versionName);??
  145. ????????????????infos.put("versionCode",?versionCode);??
  146. ????????????}??
  147. ????????}?catch?(NameNotFoundException?e)?{??
  148. ????????????Log.e(TAG,?"an?error?occured?when?collect?package?info",?e);??
  149. ????????}??
  150. ??
  151. ????????Field[]?fields?=?Build.class.getDeclaredFields();??
  152. ????????for?(Field?field?:?fields)?{??
  153. ????????????try?{??
  154. ????????????????field.setAccessible(true);??
  155. ????????????????infos.put(field.getName(),?field.get(null).toString());??
  156. ????????????????Log.d(TAG,?field.getName()?+?"?:?"?+?field.get(null));??
  157. ????????????}?catch?(Exception?e)?{??
  158. ????????????????Log.e(TAG,?"an?error?occured?when?collect?crash?info",?e);??
  159. ????????????}??
  160. ????????}??
  161. ????}??
  162. ??
  163. ????/**?
  164. ?????*?保存错误信息到文件中?
  165. ????*?
  166. ?????*?@param?ex?
  167. ?????*?@return??返回文件名称,便于将文件传送到服务器?
  168. ?????*/??
  169. ????private?String?saveCrashInfo2File(Throwable?ex)?{??
  170. ????????StringBuffer?sb?=?new?StringBuffer();??
  171. ????????for?(Map.Entry<String,?String>?entry?:?infos.entrySet())?{??
  172. ????????????String?key?=?entry.getKey();??
  173. ????????????String?value?=?entry.getValue();??
  174. ????????????sb.append(key?+?"="?+?value?+?"\n");??
  175. ????????}??
  176. ??
  177. ????????Writer?writer?=?new?StringWriter();??
  178. ????????PrintWriter?printWriter?=?new?PrintWriter(writer);??
  179. ????????ex.printStackTrace(printWriter);??
  180. ????????Throwable?cause?=?ex.getCause();??
  181. ????????while?(cause?!=?null)?{??
  182. ????????????cause.printStackTrace(printWriter);??
  183. ????????????cause?=?cause.getCause();??
  184. ????????}??
  185. ????????printWriter.close();??
  186. ??
  187. ????????String?result?=?writer.toString();??
  188. ????????sb.append(result);??
  189. ????????try?{??
  190. ????????????long?timestamp?=?System.currentTimeMillis();??
  191. ????????????String?time?=?formatter.format(new?Date());??
  192. ????????????String?fileName?=?"crash-"?+?time?+?"-"?+?timestamp?+?".log";??
  193. ??????????????
  194. ????????????if?(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))?{??
  195. ????????????????String?path?=?"/sdcard/crash/";??
  196. ????????????????File?dir?=?new?File(path);??
  197. ????????????????if?(!dir.exists())?{??
  198. ????????????????????dir.mkdirs();??
  199. ????????????????}??
  200. ????????????????FileOutputStream?fos?=?new?FileOutputStream(path?+?fileName);??
  201. ????????????????fos.write(sb.toString().getBytes());??
  202. ????????????????fos.close();??
  203. ????????????}??
  204. ??
  205. ????????????return?fileName;??
  206. ????????}?catch?(Exception?e)?{??
  207. ????????????Log.e(TAG,?"an?error?occured?while?writing?file...",?e);??
  208. ????????}??
  209. ??
  210. ????????return?null;??
  211. ????}??
  212. }??

?

?

在收集异常信息时,朋友们也可以使用 Properties,因为 Properties 有一个很便捷的方法 properties.store(OutputStream out, String comments),用来将Properties 实例中的键值对外输到输出流中,但是在使用的过程中发现生成的文件中异常信息打印在同一行,看起来极为费劲,所以换成 Map 来存放这些信息,然后生成文件时稍加了些操作。

?

完成这个 CrashHandler 后,我们需要在一个 Application 环境中让其运行,为此,我们继承 android.app.Application,添加自己的代码,CrashApplication.java代码如下:

?

[java]?view plaincopy
  1. package?com.scott.crash;??
  2. ??
  3. import?android.app.Application;??
  4. ??
  5. public?class?CrashApplication?extends?Application?{??
  6. ??????
  7. ????@Override??
  8. ????public?void?onCreate()?{??
  9. ????????super.onCreate();??
  10. ????????CrashHandler?crashHandler?=?CrashHandler.getInstance();??
  11. ????????crashHandler.init(getApplicationContext());??
  12. ????}??
  13. ??
  14. }??

?

最后,为了让我们的 CrashApplication 取代 android.app.Application 的地位,在我们的代码中生效,我们需要修改 AndroidManifest.xml:

?

[java]?view plaincopy
  1. <application?android:name=".CrashApplication"?...>??
  2. </application>???

?

因为我们上面的 CrashHandler 中,遇到异常后要保存设备参数和具体异常信息到 SDCARD,所以我们需要在 AndroidManifest.xml 中加入读写 SDCARD 权限:

?

[java]?view plaincopy
  1. <uses-permission?android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>??

?

搞定了上边的步骤之后,我们来运行一下这个项目:

在Android中自定义捕获Application全局错误,可以替换掉系统的强制退出对话框(很有参考价值与实用价值)

?

可以看到,并不会有强制关闭的对话框出现了,取而代之的是我们比较有好的提示信息,然后看一下 SDCARD 生成的文件:

在Android中自定义捕获Application全局错误,可以替换掉系统的强制退出对话框(很有参考价值与实用价值)

?

用文本编辑器打开日志文件,看一段日志信息:

?

[java]?view plaincopy
  1. CPU_ABI=armeabi??
  2. CPU_ABI2=unknown??
  3. ID=FRF91??
  4. MANUFACTURER=unknown??
  5. BRAND=generic??
  6. TYPE=eng??
  7. ......??
  8. Caused?by:?java.lang.NullPointerException????
  9. ????at?com.scott.crash.MainActivity.onCreate(MainActivity.java:13)????
  10. ????at?android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)????
  11. ????at?android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2627)????
  12. ????...?11?more??

?

?

这些信息对于开发者来说帮助极大,所以我们需要将此日志文件上传到服务器,有关文件上传的技术,请参照 Android 中使用 HTTP 服务相关介绍。

不过在使用 HTTP 服务之前,需要确定网络畅通,我们可以使用下面的方式判断网络是否可用:

?

[java]?view plaincopy
  1. /**?
  2. ?*?网络是否可用?
  3. ?*?
  4. ?*?@param?context?
  5. ?*?@return?
  6. ?*/??
  7. public?static?boolean?isNetworkAvailable(Context?context)?{??
  8. ????ConnectivityManager?mgr?=?(ConnectivityManager)?context.getSystemService(Context.CONNECTIVITY_SERVICE);??
  9. ????NetworkInfo[]?info?=?mgr.getAllNetworkInfo();??
  10. ????if?(info?!=?null)?{??
  11. ????????for?(int?i?=?0;?i?<?info.length;?i++)?{??
  12. ????????????if?(info[i].getState()?==?NetworkInfo.State.CONNECTED)?{??
  13. ????????????????return?true;??
  14. ????????????}??
  15. ????????}??
  16. ????}??
  17. ????return?false;??
  18. }??

热点排行