一:为什么要处理?

其实我们都知道,在开发过程中,自己的app系统或许有许多隐藏的异常,自己没有捕捉到,那么关于异常的捕捉,这是相当重要的,如果系统发生崩溃,那么至少也可以让系统挂在系统之内,不会发现什么系统直接退了,或者是卡死,这样做,能够使得用户体验效果更加,自己也可以发现用户到底出现什么异常,便于自己以后好处理这个问题,优化处理自己的系统。

二:如何解决

在Android 开发中,自身其实带有一个系统默认的异常处理接口,UncaughtExceptionHandler,该接口呢,能够很好的处理程序中发生的异常,所以,往往开发者都喜欢使用它,而且它也是一个非常简单好用的东西。

三:具体实现

(1)实现UncaughtExceptionHandler接口的类

  1. package com.x1.tools;
  2.  
  3. import java.lang.Thread.UncaughtExceptionHandler;
  4.  
  5. import android.app.ActivityManager;
  6. import android.content.ComponentName;
  7. import android.content.Context;
  8. import android.os.Looper;
  9. import android.util.Log;
  10.  
  11. import com.x1.dao.SubmitConfigBugs;
  12. import com.x1.ui.R;
  13.  
  14. /**
  15. * 未捕获异常捕捉类
  16. *
  17. * @author zengtao 2015年5月6日
  18. *
  19. *
  20. */
  21. public class CrashHandlers implements UncaughtExceptionHandler {
  22.  
  23. public static final String TGA = "CrashHandlers";
  24.  
  25. // 系统默认的UncaughtException处理类
  26. private Thread.UncaughtExceptionHandler mDefaultHandler;
  27.  
  28. // CrashHandler实例
  29. private static CrashHandlers instance;
  30. // 程序的Context对象
  31. private Context mContext;
  32.  
  33. private GetPhoneInfo phone;
  34.  
  35. /** 保证只有一个CrashHandler实例 */
  36. private CrashHandlers() {
  37. }
  38.  
  39. /** 获取CrashHandler实例 ,单例模式 */
  40. public synchronized static CrashHandlers getInstance() {
  41. if (instance == null) {
  42. instance = new CrashHandlers();
  43. }
  44. return instance;
  45. }
  46.  
  47. /**
  48. * 初始化
  49. *
  50. * @param context
  51. */
  52. public void init(Context context) {
  53. mContext = context;
  54. // 获取系统默认的UncaughtException处理器
  55. mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
  56. // 设置该CrashHandler为程序的默认处理器
  57. Thread.setDefaultUncaughtExceptionHandler(this);
  58. phone = new GetPhoneInfo(context);
  59. }
  60.  
  61. /**
  62. * 当UncaughtException发生时会转入该函数来处理
  63. */
  64. @Override
  65. public void uncaughtException(Thread thread, Throwable ex) {
  66. if (!handleException(thread, ex) && mDefaultHandler != null) {
  67. // 如果用户没有处理则让系统默认的异常处理器来处理
  68. mDefaultHandler.uncaughtException(thread, ex);
  69. } else {
  70. try {
  71. Thread.sleep(3000);
  72. } catch (InterruptedException e) {
  73. Log.e(TGA, e.toString());
  74. }
  75. // 退出程序
  76. android.os.Process.killProcess(android.os.Process.myPid());
  77. System.exit(1);
  78. }
  79. }
  80.  
  81. /**
  82. * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.
  83. *
  84. * @param ex
  85. * @return true:如果处理了该异常信息;否则返回false.
  86. */
  87. private boolean handleException(Thread thread, Throwable ex) {
  88. if (ex == null) {
  89. return false;
  90. }
  91. // 使用Toast来显示异常信息
  92. new Thread() {
  93. @Override
  94. public void run() {
  95. Looper.prepare();
  96. ShowToast.show(mContext, "喵,很抱歉,程序出现异常,即将退出!",
  97. R.drawable.error_icon);
  98. Looper.loop();
  99. }
  100. }.start();
  101. // 把异常信息和设备信息上传到服务器
  102. subMitThreadAndDeviceInfo(mContext, thread, ex);
  103. return true;
  104. }
  105.  
  106. // 提交信息到服务器
  107. public void subMitThreadAndDeviceInfo(Context ctx, Thread thread,
  108. Throwable ex) {
  109. // 当前用户的账号
  110. String Account = null;
  111. if (Config.getCachedAccount(ctx) != null) {
  112. Account = Config.getCachedAccount(ctx);
  113. } else {
  114. Account = "当前无用户登录";
  115. }
  116. // 手机设备的信息
  117. String PhoneModel = phone.PhoneModel;
  118. String PhoneVersion = phone.PhoneVersion;
  119. String PhoneResolution = phone.PhoneResolution;
  120. String ZcmVersion = phone.ZcmVersion;
  121. String AvailableRom = phone.AvailableRom;
  122. // 获取当前显示在界面上的Activity的路径加类名
  123. ActivityManager am = (ActivityManager) ctx
  124. .getSystemService(Context.ACTIVITY_SERVICE);
  125. ComponentName cn = am.getRunningTasks(1).get(0).topActivity;
  126. // 异常信息加异常所在类的绝对路径
  127. final String content = "thread: " + thread + " , name: "
  128. + thread.getName() + ", id: " + thread.getId()
  129. + ", exception: " + ex + ", " + cn.getClassName();
  130.  
  131. // 执行接口,把异常信息提交到服务器
  132. new SubmitConfigBugs(0, ctx, Account, PhoneModel, PhoneVersion,
  133. PhoneResolution, ZcmVersion, AvailableRom, content,
  134. new SubmitConfigBugs.SuccessCallback() {
  135. @Override
  136. public void onSuccess() {
  137. Log.e(TGA, content + "\n错误信息提交成功");
  138. }
  139. }, new SubmitConfigBugs.FailCallback() {
  140.  
  141. @Override
  142. public void onFail() {
  143. Log.e(TGA, content + "\n错误信息提交失败");
  144. }
  145. });
  146. }
  147.  
  148. }

(2)实体类:手机信息

  1. package com.x1.tools;
  2.  
  3. import android.content.Context;
  4. import android.content.pm.PackageInfo;
  5. import android.content.pm.PackageManager;
  6. import android.os.Environment;
  7. import android.os.StatFs;
  8. import android.text.TextUtils;
  9. import android.view.Display;
  10. import android.view.WindowManager;
  11.  
  12. /**
  13. * 获取当前手机的设备信息和当前软件的版本
  14. *
  15. * @author zengtao 2015年5月6日
  16. *
  17. *
  18. */
  19. public class GetPhoneInfo {
  20. private Context context;
  21. public String PhoneModel;
  22. public String PhoneVersion;
  23. public String PhoneResolution;
  24. public String ZcmVersion;
  25. public String AvailableRom;
  26.  
  27. public GetPhoneInfo(Context context) {
  28. this.context = context;
  29. PhoneModel = android.os.Build.MODEL;
  30. PhoneVersion = android.os.Build.VERSION.RELEASE;
  31. PhoneResolution = getDisplayWAndH();
  32. ZcmVersion = getAppVersionName(this.context);
  33. AvailableRom = "ROM剩余存储空间: " + getAvailableInternalMemorySize() + "MB"
  34. + ",内置SDCard剩余存储空间: " + getAvailableExternalMemorySize() + "MB";
  35. }
  36.  
  37. // 获取当前版本号
  38. private String getAppVersionName(Context context) {
  39. String versionName = "";
  40. try {
  41. PackageManager packageManager = context.getPackageManager();
  42. PackageInfo packageInfo = packageManager.getPackageInfo(
  43. "com.x1.ui", 0);
  44. versionName = packageInfo.versionName;
  45. if (TextUtils.isEmpty(versionName)) {
  46. return "";
  47. }
  48. } catch (Exception e) {
  49. e.printStackTrace();
  50. }
  51. return versionName;
  52. }
  53.  
  54. // 获取屏幕分辨率
  55. @SuppressWarnings("deprecation")
  56. private String getDisplayWAndH() {
  57. WindowManager wm = (WindowManager) context
  58. .getSystemService(Context.WINDOW_SERVICE);
  59. Display display = wm.getDefaultDisplay();
  60. String string = "屏幕分辨率: " + display.getWidth() + "x"
  61. + display.getHeight();
  62. return string;
  63. }
  64.  
  65. /**
  66. *
  67. * @return ROM存储路径
  68. */
  69. private String getInternalMemoryPath() {
  70. return Environment.getDataDirectory().getPath();
  71. }
  72.  
  73. /**
  74. *
  75. * @return 内置sd卡路径
  76. */
  77. private String getExternalMemoryPath() {
  78. return Environment.getExternalStorageDirectory().getPath();
  79. }
  80.  
  81. /**
  82. *
  83. * @param path
  84. * 文件路径
  85. * @return 文件路径的StatFs对象
  86. * @throws Exception
  87. * 路径为空或非法异常抛出
  88. */
  89. private StatFs getStatFs(String path) {
  90. try {
  91. return new StatFs(path);
  92. } catch (Exception e) {
  93. e.printStackTrace();
  94. }
  95. return null;
  96. }
  97.  
  98. /**
  99. *
  100. * @param stat
  101. * 文件StatFs对象
  102. * @return 剩余存储空间的MB数
  103. *
  104. */
  105. @SuppressWarnings("deprecation")
  106. private float calculateSizeInMB(StatFs stat) {
  107. if (stat != null)
  108. return stat.getAvailableBlocks()
  109. * (stat.getBlockSize() / (1024f * 1024f));
  110. return 0.0f;
  111. }
  112.  
  113. /**
  114. *
  115. * @return ROM剩余存储空间的MB数
  116. */
  117. private float getAvailableInternalMemorySize() {
  118.  
  119. String path = getInternalMemoryPath();// 获取数据目录
  120. StatFs stat = getStatFs(path);
  121. return calculateSizeInMB(stat);
  122. }
  123.  
  124. /**
  125. *
  126. * @return 内置SDCard剩余存储空间MB数
  127. */
  128. private float getAvailableExternalMemorySize() {
  129.  
  130. String path = getExternalMemoryPath();// 获取数据目录
  131. StatFs stat = getStatFs(path);
  132. return calculateSizeInMB(stat);
  133.  
  134. }
  135. }

  

  1. package com.x1.tools;
  2. import java.lang.Thread.UncaughtExceptionHandler;
  3. import android.app.ActivityManager;
  4. import android.content.ComponentName;
  5. import android.content.Context;
  6. import android.os.Looper;
  7. import android.util.Log;
  8. import com.x1.dao.SubmitConfigBugs;
  9. import com.x1.ui.R;
  10. /**
  11. * 未捕获异常捕捉类
  12. *
  13. * @author zengtao 2015年5月6日
  14. *
  15. *
  16. */
  17. public class CrashHandlers implements UncaughtExceptionHandler {
  18. public static final String TGA = "CrashHandlers";
  19. // 系统默认的UncaughtException处理类
  20. private Thread.UncaughtExceptionHandler mDefaultHandler;
  21. // CrashHandler实例
  22. private static CrashHandlers instance;
  23. // 程序的Context对象
  24. private Context mContext;
  25. private GetPhoneInfo phone;
  26. /** 保证只有一个CrashHandler实例 */
  27. private CrashHandlers() {
  28. }
  29. /** 获取CrashHandler实例 ,单例模式 */
  30. public synchronized static CrashHandlers getInstance() {
  31. if (instance == null) {
  32. instance = new CrashHandlers();
  33. }
  34. return instance;
  35. }
  36. /**
  37. * 初始化
  38. *
  39. * @param context
  40. */
  41. public void init(Context context) {
  42. mContext = context;
  43. // 获取系统默认的UncaughtException处理器
  44. mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
  45. // 设置该CrashHandler为程序的默认处理器
  46. Thread.setDefaultUncaughtExceptionHandler(this);
  47. phone = new GetPhoneInfo(context);
  48. }
  49. /**
  50. * 当UncaughtException发生时会转入该函数来处理
  51. */
  52. @Override
  53. public void uncaughtException(Thread thread, Throwable ex) {
  54. if (!handleException(thread, ex) && mDefaultHandler != null) {
  55. // 如果用户没有处理则让系统默认的异常处理器来处理
  56. mDefaultHandler.uncaughtException(thread, ex);
  57. } else {
  58. try {
  59. Thread.sleep(3000);
  60. } catch (InterruptedException e) {
  61. Log.e(TGA, e.toString());
  62. }
  63. // 退出程序
  64. android.os.Process.killProcess(android.os.Process.myPid());
  65. System.exit(1);
  66. }
  67. }
  68. /**
  69. * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.
  70. *
  71. * @param ex
  72. * @return true:如果处理了该异常信息;否则返回false.
  73. */
  74. private boolean handleException(Thread thread, Throwable ex) {
  75. if (ex == null) {
  76. return false;
  77. }
  78. // 使用Toast来显示异常信息
  79. new Thread() {
  80. @Override
  81. public void run() {
  82. Looper.prepare();
  83. ShowToast.show(mContext, "喵,很抱歉,程序出现异常,即将退出!",
  84. R.drawable.error_icon);
  85. Looper.loop();
  86. }
  87. }.start();
  88. // 把异常信息和设备信息上传到服务器
  89. subMitThreadAndDeviceInfo(mContext, thread, ex);
  90. return true;
  91. }
  92. // 提交信息到服务器
  93. public void subMitThreadAndDeviceInfo(Context ctx, Thread thread,
  94. Throwable ex) {
  95. // 当前用户的账号
  96. String Account = null;
  97. if (Config.getCachedAccount(ctx) != null) {
  98. Account = Config.getCachedAccount(ctx);
  99. } else {
  100. Account = "当前无用户登录";
  101. }
  102. // 手机设备的信息
  103. String PhoneModel = phone.PhoneModel;
  104. String PhoneVersion = phone.PhoneVersion;
  105. String PhoneResolution = phone.PhoneResolution;
  106. String ZcmVersion = phone.ZcmVersion;
  107. String AvailableRom = phone.AvailableRom;
  108. // 获取当前显示在界面上的Activity的路径加类名
  109. ActivityManager am = (ActivityManager) ctx
  110. .getSystemService(Context.ACTIVITY_SERVICE);
  111. ComponentName cn = am.getRunningTasks(1).get(0).topActivity;
  112. // 异常信息加异常所在类的绝对路径
  113. final String content = "thread: " + thread + " , name: "
  114. + thread.getName() + ", id: " + thread.getId()
  115. + ", exception: " + ex + ", " + cn.getClassName();
  116. // 执行接口,把异常信息提交到服务器
  117. new SubmitConfigBugs(0, ctx, Account, PhoneModel, PhoneVersion,
  118. PhoneResolution, ZcmVersion, AvailableRom, content,
  119. new SubmitConfigBugs.SuccessCallback() {
  120. @Override
  121. public void onSuccess() {
  122. Log.e(TGA, content + "\n错误信息提交成功");
  123. }
  124. }, new SubmitConfigBugs.FailCallback() {
  125. @Override
  126. public void onFail() {
  127. Log.e(TGA, content + "\n错误信息提交失败");
  128. }
  129. });
  130. }
  131. }

Android 捕捉app系统中未处理的异常的更多相关文章

  1. Android实现app长时间未操作时自动退出app

    这里要考虑3个问题,第一个是锁屏问题,第二个是app被切换至后台的问题,第三个是屏幕锁定和解除时app在后台时的问题 一,监听屏幕解锁,锁定 ? 1 2 3 4 5 6 7 8 9 10 11 12 ...

  2. C# 截获某个域中未捕获的异常 CLR20R3 程序终止的几种解决方案

    AppDomain.UnhandledException可以获的异常,却截不下来,求解 AppDomain.CurrentDomain.UnhandledException += CurrentDom ...

  3. Android应用图标微技巧,8.0系统中应用图标的适配

    现在已经进入了2018年,Android 8.0系统也逐渐开始普及起来了.三星今年推出的最新旗舰机Galaxy S9已经搭载了Android 8.0系统,紧接着小米.华为.OV等国产手机厂商即将推出的 ...

  4. Android 8.0系统的应用图标适配

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 概述 参考资料<一起来学习Android 8.0系统的应用图标适配吧>中已经讲得很清楚了,这里我只是简单总结下.详情的内容请阅 ...

  5. Android中使用UncaughtExceptionHandler来处理未捕获的异常

    原文在sparkyuan.me上.转载注明出处:http://sparkyuan.github.io/2016/03/28/使用UncaughtExceptionHandler来处理未捕获的异常/ 全 ...

  6. app测试中,ios和android的区别

    App测试中ios和Android的区别: 1. Android长按home键呼出应用列表和切换应用,然后右滑则终止应用: 2. 多分辨率测试,Android端20多种,ios较少: 3. 手机操作系 ...

  7. Android App在Google App Store中搜不到

    情景:Android App在Google App Store上架成功,三星手机可以在Google App Store中搜索到,但是三星tablet却无法在Google App Store中搜索到,目 ...

  8. Android系统中自定义按键的短按、双击、长按事件

    在项目中碰到这样的问题: 由于系统中的按键在底层做了重新定义或者新增了按键,此时需要在APP层对按键事件(keyevent)做分解处理,模拟Android系统做法,把keyevent分解成: 1.单击 ...

  9. [原创]Android系统中常用JAVA类源码浅析之HashMap

    由于是浅析,所以我只分析常用的接口,注意是Android系统中的JAVA类,可能和JDK的源码有区别. 首先从构造函数开始, /** * Min capacity (other than zero) ...

随机推荐

  1. 【Offer】[41] 【数据流中的中位数】

    题目描述 思路分析 测试用例 Java代码 代码链接 题目描述 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值.如果从数据流中读出偶数个数值, ...

  2. bluetooth(蓝牙) AVRCP协议概念及代码流程解析

    一 概念 AVRCP全称:The Audio/Video Remote Control Profile (AVRCP) 翻译成中文就是:音视频远程控制协议.概念:AVRCP定义了蓝牙设备之间的音视频传 ...

  3. kafka经典入门

    问题导读 1.Kafka独特设计在什么地方?2.Kafka如何搭建及创建topic.发送消息.消费消息?3.如何书写Kafka程序?4.数据传输的事务定义有哪三种?5.Kafka判断一个节点是否活着有 ...

  4. 字节输出流OutputStream

    1.OutputStream是输出字节流的超类. import java.io.File; import java.io.FileOutputStream; import java.io.IOExce ...

  5. 小白专场-是否同一颗二叉搜索树-python语言实现

    目录 一.二叉搜索树的相同判断 二.问题引入 三.举例分析 四.方法探讨 4.1 中序遍历 4.2 层序遍历 4.3 先序遍历 4.4 后序遍历 五.总结 六.代码实现 一.二叉搜索树的相同判断 二叉 ...

  6. 第二次实验报告:使用Packet Tracer分析应用层协议

    个人信息:      •  姓名:李微微       •  班级:计算1811       •  学号:201821121001 一.摘要 本文描述使用Packet Tracer,正确配置网络参数,抓 ...

  7. Linux之正则表达式grep

    真好!

  8. Recovery启动流程--recovery.cpp分析

    这篇文章主要通过分析高通recovery目录下的recovery.cpp源码,对recovery启动流程有一个宏观的了解. 当开机以后,在lk阶段,如果是recovery,会设置boot_into_r ...

  9. java枚举的应用

    最近的项目中,看前辈们用到的枚举比较多,由于自己之前对枚举这种类型不是很了解,遂花费心机看了下,整理记录下. 1.枚举常量 系统中定义的状态字段,用的比较多: public enum orderTyp ...

  10. (转)在阿里云 CentOS 服务器(ECS)上搭建 nginx + mysql + php-fpm 环境

    阿里云的云服务器(ECS)可以选择多种操作系统,打算用它运行 Drupal或者 WordPress ,你最好选择 Linux 系统,这篇文章的演示是基于阿里云的 CentOS 操作系统的服务器.我们在 ...