`
king_tt
  • 浏览: 2108414 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

Android全局处理异常

 
阅读更多

CrashHandler

Java代码收藏代码
  1. packageorg.wp.activity;
  2. importjava.io.File;
  3. importjava.io.FileOutputStream;
  4. importjava.io.FilenameFilter;
  5. importjava.io.PrintWriter;
  6. importjava.io.StringWriter;
  7. importjava.io.Writer;
  8. importjava.lang.Thread.UncaughtExceptionHandler;
  9. importjava.lang.reflect.Field;
  10. importjava.util.Arrays;
  11. importjava.util.Properties;
  12. importjava.util.TreeSet;
  13. importandroid.content.Context;
  14. importandroid.content.pm.PackageInfo;
  15. importandroid.content.pm.PackageManager;
  16. importandroid.content.pm.PackageManager.NameNotFoundException;
  17. importandroid.os.Build;
  18. importandroid.os.Looper;
  19. importandroid.util.Log;
  20. importandroid.widget.Toast;
  21. /**
  22. *
  23. *
  24. *UncaughtExceptionHandler:线程未捕获异常控制器是用来处理未捕获异常的。
  25. *如果程序出现了未捕获异常默认情况下则会出现强行关闭对话框
  26. *实现该接口并注册为程序中的默认未捕获异常处理
  27. *这样当未捕获异常发生时,就可以做些异常处理操作
  28. *例如:收集异常信息,发送错误报告等。
  29. *
  30. *UncaughtException处理类,当程序发生Uncaught异常的时候,由该类来接管程序,并记录发送错误报告.
  31. */
  32. publicclassCrashHandlerimplementsUncaughtExceptionHandler{
  33. /**DebugLogTag*/
  34. publicstaticfinalStringTAG="CrashHandler";
  35. /**是否开启日志输出,在Debug状态下开启,在Release状态下关闭以提升程序性能*/
  36. publicstaticfinalbooleanDEBUG=true;
  37. /**CrashHandler实例*/
  38. privatestaticCrashHandlerINSTANCE;
  39. /**程序的Context对象*/
  40. privateContextmContext;
  41. /**系统默认的UncaughtException处理类*/
  42. privateThread.UncaughtExceptionHandlermDefaultHandler;
  43. /**使用Properties来保存设备的信息和错误堆栈信息*/
  44. privatePropertiesmDeviceCrashInfo=newProperties();
  45. privatestaticfinalStringVERSION_NAME="versionName";
  46. privatestaticfinalStringVERSION_CODE="versionCode";
  47. privatestaticfinalStringSTACK_TRACE="STACK_TRACE";
  48. /**错误报告文件的扩展名*/
  49. privatestaticfinalStringCRASH_REPORTER_EXTENSION=".cr";
  50. /**保证只有一个CrashHandler实例*/
  51. privateCrashHandler(){
  52. }
  53. /**获取CrashHandler实例,单例模式*/
  54. publicstaticCrashHandlergetInstance(){
  55. if(INSTANCE==null)
  56. INSTANCE=newCrashHandler();
  57. returnINSTANCE;
  58. }
  59. /**
  60. *初始化,注册Context对象,获取系统默认的UncaughtException处理器,设置该CrashHandler为程序的默认处理器
  61. *
  62. *@paramctx
  63. */
  64. publicvoidinit(Contextctx){
  65. mContext=ctx;
  66. mDefaultHandler=Thread.getDefaultUncaughtExceptionHandler();
  67. Thread.setDefaultUncaughtExceptionHandler(this);
  68. }
  69. /**
  70. *当UncaughtException发生时会转入该函数来处理
  71. */
  72. @Override
  73. publicvoiduncaughtException(Threadthread,Throwableex){
  74. if(!handleException(ex)&&mDefaultHandler!=null){
  75. //如果用户没有处理则让系统默认的异常处理器来处理
  76. mDefaultHandler.uncaughtException(thread,ex);
  77. }else{
  78. //Sleep一会后结束程序
  79. //来让线程停止一会是为了显示Toast信息给用户,然后Kill程序
  80. try{
  81. Thread.sleep(3000);
  82. }catch(InterruptedExceptione){
  83. Log.e(TAG,"Error:",e);
  84. }
  85. android.os.Process.killProcess(android.os.Process.myPid());
  86. System.exit(10);
  87. }
  88. }
  89. /**
  90. *自定义错误处理,收集错误信息发送错误报告等操作均在此完成.开发者可以根据自己的情况来自定义异常处理逻辑
  91. *
  92. *@paramex
  93. *@returntrue:如果处理了该异常信息;否则返回false
  94. */
  95. privatebooleanhandleException(Throwableex){
  96. if(ex==null){
  97. returntrue;
  98. }
  99. finalStringmsg=ex.getLocalizedMessage();
  100. //使用Toast来显示异常信息
  101. newThread(){
  102. @Override
  103. publicvoidrun(){
  104. //Toast显示需要出现在一个线程的消息队列中
  105. Looper.prepare();
  106. Toast.makeText(mContext,"程序出错啦:"+msg,Toast.LENGTH_LONG).show();
  107. Looper.loop();
  108. }
  109. }.start();
  110. //收集设备信息
  111. collectCrashDeviceInfo(mContext);
  112. //保存错误报告文件
  113. StringcrashFileName=saveCrashInfoToFile(ex);
  114. //发送错误报告到服务器
  115. sendCrashReportsToServer(mContext);
  116. returntrue;
  117. }
  118. /**
  119. *收集程序崩溃的设备信息
  120. *
  121. *@paramctx
  122. */
  123. publicvoidcollectCrashDeviceInfo(Contextctx){
  124. try{
  125. //Classforretrievingvariouskindsofinformationrelatedtothe
  126. //applicationpackagesthatarecurrentlyinstalledonthedevice.
  127. //YoucanfindthisclassthroughgetPackageManager().
  128. PackageManagerpm=ctx.getPackageManager();
  129. //getPackageInfo(StringpackageName,intflags)
  130. //Retrieveoverallinformationaboutanapplicationpackagethatisinstalledonthesystem.
  131. //publicstaticfinalintGET_ACTIVITIES
  132. //Since:APILevel1PackageInfoflag:returninformationaboutactivitiesinthepackageinactivities.
  133. PackageInfopi=pm.getPackageInfo(ctx.getPackageName(),PackageManager.GET_ACTIVITIES);
  134. if(pi!=null){
  135. //publicStringversionNameTheversionnameofthispackage,
  136. //asspecifiedbythe<manifest>tag'sversionNameattribute.
  137. mDeviceCrashInfo.put(VERSION_NAME,pi.versionName==null?"notset":pi.versionName);
  138. //publicintversionCodeTheversionnumberofthispackage,
  139. //asspecifiedbythe<manifest>tag'sversionCodeattribute.
  140. mDeviceCrashInfo.put(VERSION_CODE,pi.versionCode);
  141. }
  142. }catch(NameNotFoundExceptione){
  143. Log.e(TAG,"Errorwhilecollectpackageinfo",e);
  144. }
  145. //使用反射来收集设备信息.在Build类中包含各种设备信息,
  146. //例如:系统版本号,设备生产商等帮助调试程序的有用信息
  147. //返回Field对象的一个数组,这些对象反映此Class对象所表示的类或接口所声明的所有字段
  148. Field[]fields=Build.class.getDeclaredFields();
  149. for(Fieldfield:fields){
  150. try{
  151. //setAccessible(booleanflag)
  152. //将此对象的accessible标志设置为指示的布尔值。
  153. //通过设置Accessible属性为true,才能对私有变量进行访问,不然会得到一个IllegalAccessException的异常
  154. field.setAccessible(true);
  155. mDeviceCrashInfo.put(field.getName(),field.get(null));
  156. if(DEBUG){
  157. Log.d(TAG,field.getName()+":"+field.get(null));
  158. }
  159. }catch(Exceptione){
  160. Log.e(TAG,"Errorwhilecollectcrashinfo",e);
  161. }
  162. }
  163. }
  164. /**
  165. *保存错误信息到文件中
  166. *
  167. *@paramex
  168. *@return
  169. */
  170. privateStringsaveCrashInfoToFile(Throwableex){
  171. Writerinfo=newStringWriter();
  172. PrintWriterprintWriter=newPrintWriter(info);
  173. //printStackTrace(PrintWriters)
  174. //将此throwable及其追踪输出到指定的PrintWriter
  175. ex.printStackTrace(printWriter);
  176. //getCause()返回此throwable的cause;如果cause不存在或未知,则返回null。
  177. Throwablecause=ex.getCause();
  178. while(cause!=null){
  179. cause.printStackTrace(printWriter);
  180. cause=cause.getCause();
  181. }
  182. //toString()以字符串的形式返回该缓冲区的当前值。
  183. Stringresult=info.toString();
  184. printWriter.close();
  185. mDeviceCrashInfo.put(STACK_TRACE,result);
  186. try{
  187. longtimestamp=System.currentTimeMillis();
  188. StringfileName="crash-"+timestamp+CRASH_REPORTER_EXTENSION;
  189. //保存文件
  190. FileOutputStreamtrace=mContext.openFileOutput(fileName,Context.MODE_PRIVATE);
  191. mDeviceCrashInfo.store(trace,"");
  192. trace.flush();
  193. trace.close();
  194. returnfileName;
  195. }catch(Exceptione){
  196. Log.e(TAG,"anerroroccuredwhilewritingreportfile...",e);
  197. }
  198. returnnull;
  199. }
  200. /**
  201. *把错误报告发送给服务器,包含新产生的和以前没发送的.
  202. *
  203. *@paramctx
  204. */
  205. privatevoidsendCrashReportsToServer(Contextctx){
  206. String[]crFiles=getCrashReportFiles(ctx);
  207. if(crFiles!=null&&crFiles.length>0){
  208. TreeSet<String>sortedFiles=newTreeSet<String>();
  209. sortedFiles.addAll(Arrays.asList(crFiles));
  210. for(StringfileName:sortedFiles){
  211. Filecr=newFile(ctx.getFilesDir(),fileName);
  212. postReport(cr);
  213. cr.delete();//删除已发送的报告
  214. }
  215. }
  216. }
  217. /**
  218. *获取错误报告文件名
  219. *
  220. *@paramctx
  221. *@return
  222. */
  223. privateString[]getCrashReportFiles(Contextctx){
  224. FilefilesDir=ctx.getFilesDir();
  225. //实现FilenameFilter接口的类实例可用于过滤器文件名
  226. FilenameFilterfilter=newFilenameFilter(){
  227. //accept(Filedir,Stringname)
  228. //测试指定文件是否应该包含在某一文件列表中。
  229. publicbooleanaccept(Filedir,Stringname){
  230. returnname.endsWith(CRASH_REPORTER_EXTENSION);
  231. }
  232. };
  233. //list(FilenameFilterfilter)
  234. //返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中满足指定过滤器的文件和目录
  235. returnfilesDir.list(filter);
  236. }
  237. privatevoidpostReport(Filefile){
  238. //TODO使用HTTPPost发送错误报告到服务器
  239. //这里不再详述,开发者可以根据OPhoneSDN上的其他网络操作
  240. //教程来提交错误报告
  241. }
  242. /**
  243. *在程序启动时候,可以调用该函数来发送以前没有发送的报告
  244. */
  245. publicvoidsendPreviousReportsToServer(){
  246. sendCrashReportsToServer(mContext);
  247. }
  248. }

CrashApplication

Java代码收藏代码
  1. packageorg.wp.activity;
  2. importandroid.app.Application;
  3. /**
  4. *在开发应用时都会和Activity打交道,而Application使用的就相对较少了。
  5. *Application是用来管理应用程序的全局状态的,比如载入资源文件。
  6. *在应用程序启动的时候Application会首先创建,然后才会根据情况(Intent)启动相应的Activity或者Service。
  7. *在本文将在Application中注册未捕获异常处理器。
  8. */
  9. publicclassCrashApplicationextendsApplication{
  10. @Override
  11. publicvoidonCreate(){
  12. super.onCreate();
  13. CrashHandlercrashHandler=CrashHandler.getInstance();
  14. //注册crashHandler
  15. crashHandler.init(getApplicationContext());
  16. //发送以前没发送的报告(可选)
  17. crashHandler.sendPreviousReportsToServer();
  18. }
  19. }

在AndroidManifest.xml中注册

Xml代码收藏代码
  1. <?xmlversion="1.0"encoding="utf-8"?>
  2. <manifestxmlns:android="http://schemas.android.com/apk/res/android"
  3. package="org.wp.activity"android:versionCode="1"android:versionName="1.0">
  4. <applicationandroid:icon="@drawable/icon"android:label="@string/app_name"
  5. android:name=".CrashApplication"android:debuggable="true">
  6. <activityandroid:name=".MainActivity"android:label="@string/app_name">
  7. <intent-filter>
  8. <actionandroid:name="android.intent.action.MAIN"/>
  9. <categoryandroid:name="android.intent.category.LAUNCHER"/>
  10. </intent-filter>
  11. </activity>
  12. </application>
  13. <uses-sdkandroid:minSdkVersion="8"/>
  14. </manifest>

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics