Exception

Java的异常分两类,运行时异常RuntimeException和非运行时异常。
运行时异常包括空指针异常NullPointerException、数组越界异常IndexOutOfBoundsException、类型转换异常ClassCastException、数据库异常SQLException等等,(网上很多文章把SQLException归为非运行时异常,但查看源码SQLException继承自RuntimeException,所以它应是运行时异常)。非运行时异常包括输入输出异常IOException、无此加密算法异常NoSuchAlgorithmException等等。
非运行时异常在编码的时候就要进行处理,不然编译都通不过。运行时异常有的在程序运行时才会发现,但也有的在编码时就得处理,比如说非法参数异常IllegalArgumentException、非法状态异常IllegalStateException等等。

下面是代码中处理异常的一些注意事项:
1、只在必须处理异常的地方才使用异常,不要把业务逻辑写在catch块中;
2、切忌使用空的catch块,空块看起来很爽,可一旦出现错误将难以排查;
3、注意在finally块中释放资源,比如拍照时发生异常,务必要释放摄像头资源,避免资源被锁;

不管怎么处理异常,都属于事后的亡羊补牢,并不是什么好办法。最好的办法是未雨绸缪,防患于未然,处理异常不如预防异常。所以如果可以的话,尽量在代码中预先判断条件是否合法,不要等到程序扔出异常时才处理,例如:
1、使用某对象的方法或属性时,要先判断该对象是否为空,避免扔出空指针异常;
2、使用下标访问数组元素时,要先判断下标是否大于数组长度,避免扔出数组越界异常;
3、在转换对象类型时,要先用instanof关键字判断类型是否正确,避免扔出类型转换异常;
4、在访问文件时,要先用exists方法判断文件是否存在,避免扔出文件不存在异常;

CrashHandler

人算不如天算,程序代码写得再无懈可击,运行起来也可能出现未知异常。一旦遇见异常,表示app已无条件继续运行,该闪退的闪退,该提示用户的提示用户。可是我们开发者都想知道用户手机上发生了什么情况,导致app异常退出,所谓吃一堑长一智,发现问题、总结问题才能逐步提高嘛。现在的问题就是我们如何才能让app自动把未知异常记录下来,并同时保存案发现场的环境信息,这样后续才有机会把异常报告传回给服务器。

自动捕获未知异常的主要思路是,在Application注册一个实现了UncaughtExceptionHandler的对象,然后在该对象中调用方法Thread.setDefaultUncaughtExceptionHandler设置未知异常的处理器;同时该对象自身需实现uncaughtException方法,在uncaughtException方法中记录异常信息,以及设备的环境信息,所有这些信息保存在本地的文件中。如果仅仅是调试使用,这样处理就差不多了;如果用于正式上线的app,那还得择机把异常信息文件传回服务器。

不啰嗦废话了,下面贴上工具类CrashHandler的示例代码:

import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.Thread.UncaughtExceptionHandler;
import java.lang.reflect.Field;import com.example.exmexception.except.util.PropertiesUtil;
import com.example.exmexception.except.util.Utils;import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Build;
import android.os.Looper;
import android.os.Process;
import android.util.Log;
import android.widget.Toast;public class CrashHandler implements UncaughtExceptionHandler {public static final String TAG = "CrashHandler";//是否开启日志输出,在Debug状态下开启public static final boolean DEBUG = true;//系统默认的UncaughtException处理类private Thread.UncaughtExceptionHandler mDefaultHandler;//程序的Context对象private Context mContext;private static final String VERSION_NAME = "versionName";private static final String VERSION_CODE = "versionCode";private static final String STACK_TRACE = "STACK_TRACE";private PropertiesUtil mProp;//CrashHandler实例private static CrashHandler INSTANCE;//获取CrashHandler实例,单例模式public static CrashHandler getInstance() {if (INSTANCE == null) {INSTANCE = new CrashHandler();}return INSTANCE;}//获取系统默认的UncaughtException处理器,设置该CrashHandler为程序的默认处理器 public void setCrashHandler(Context ctx) {Log.d(TAG, "CrashHandler setCrashHandler");mContext = ctx;mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();Thread.setDefaultUncaughtExceptionHandler(this);mProp = PropertiesUtil.getInstance(ctx);}//当UncaughtException发生时会转入该函数来处理@Override    public void uncaughtException(Thread thread, Throwable ex) {Log.d(TAG, "CrashHandler uncaughtException");if (DEBUG) {ex.printStackTrace();}if (!handleException(ex) && mDefaultHandler != null) {Log.d(TAG, "mDefaultHandler.uncaughtException");//如果用户没有处理则让系统默认的异常处理器来处理    mDefaultHandler.uncaughtException(thread, ex);} else {Log.d(TAG, "sleep and killProcess");try {Thread.sleep(2000);}catch (InterruptedException e) {Log.e(TAG, "Error : ", e);}Process.killProcess(Process.myPid());System.exit(10);}}private String getMsg(Throwable ex) {//若是空指针异常,getLocalizedMessage返回的是nullString msg = ex.getLocalizedMessage();if (msg == null) {
//          PrintStream err_msg = System.err.append(toString());
//          msg = err_msg.toString();StackTraceElement[] stackArray = ex.getStackTrace();StackTraceElement element = stackArray[0];msg = element.toString();}return msg;}//自定义错误处理、收集错误信息、发送错误报告等操作均在此完成private boolean handleException(Throwable ex) {if (ex == null) {Log.d(TAG, "handleException --- ex==null");return true;}final String msg = getMsg(ex);if(msg == null) {Log.d(TAG, "getMessage is null");return false;}new Thread() {@Override    public void run() {Looper.prepare();Toast.makeText(mContext, "程序出错,即将退出:\n"+msg, Toast.LENGTH_LONG).show();Looper.loop();}}.start();String file_name = String.format("crash-%s.log", Utils.getNowDateTime());mProp.setFile(file_name).init();//收集设备信息    collectCrashDeviceInfo(mContext);//保存错误报告文件    saveCrashInfoToFile(ex);//保存错误信息mProp.commit();//发送错误报告到服务器,若后台需要获取错误报告则打开//sendCrashReportsToServer(mContext);return true;}//保存错误信息到文件中 private void saveCrashInfoToFile(Throwable ex) {Log.d(TAG, "saveCrashInfoToFile");Writer info = new StringWriter();PrintWriter printWriter = new PrintWriter(info);ex.printStackTrace(printWriter);Throwable cause = ex.getCause();while (cause != null) {cause.printStackTrace(printWriter);cause = cause.getCause();}String result = info.toString();printWriter.close();mProp.writeString("EXEPTION", getMsg(ex));mProp.writeString(STACK_TRACE, result);}//收集程序崩溃的设备信息 private void collectCrashDeviceInfo(Context ctx) {Log.d(TAG, "collectCrashDeviceInfo");try {PackageManager pm = ctx.getPackageManager();PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), PackageManager.GET_ACTIVITIES);if (pi != null) {mProp.writeString(VERSION_NAME, (pi.versionName==null)?"not set":pi.versionName);mProp.writeInt(VERSION_CODE, pi.versionCode);}}catch (NameNotFoundException e) {Log.e(TAG, "Error while collect package info", e);}//使用反射来收集设备信息,例如:系统版本号、设备生产商等环境信息Field[] fields = Build.class.getDeclaredFields();for (Field field : fields) {try {field.setAccessible(true);mProp.writeString(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);}}}}

下面是Application类中的调用代码:

import com.example.exmexception.except.CrashHandler;import android.app.Application;public class MainApplication extends Application {@Overridepublic void onCreate() {super.onCreate();// 注册crashHandlerCrashHandler crashHandler = CrashHandler.getInstance();crashHandler.setCrashHandler(getApplicationContext());}}

点击下载本文用到的异常容错处理的工程代码

点此查看Android开发笔记的完整目录

Android开发笔记(七十八)异常容错处理相关推荐

  1. Android开发笔记(十八)书籍翻页动画PageAnimation

    前面几节的动画都算简单,本文就介绍一个复杂点的动画--书籍翻页动画.Android有自带的翻页动画ViewPager,不过ViewPager只实现了平移效果.即便使用补间组合动画或者属性动画,也只是把 ...

  2. Android开发笔记(十五)淡入淡出动画TransitionDrawable

    说到淡入淡出动画,可能大家会想到补间动画里面的AlphaAnimation,不过这个深浅动画只能对透明度做渐变效果,也就是只能对一个图形做深浅的颜色变换.如果我们想要从A图片逐渐变为B图片,也就是要实 ...

  3. Android开发笔记(九十八)往图片添加部件

    添加圆角 添加圆角的功能,要用到Canvas类的drawRoundRect方法,即把画布裁剪成指定的圆角矩形. 下面是给图片添加圆角的效果截图: 下面是给图片添加圆角的代码片段: public sta ...

  4. Android开发笔记(十九)底部标签栏TabBar

    底部标签页实现思路 现在的APP,大多在页面底部显示标签栏Tabbar,用于切换不同栏目的页面.Tabbar起源于iOS,iOS的Tabbar自动位于页面下方,可是Android搬过来的时候做了改动, ...

  5. Android开发笔记(十六)秋千摇摆动画SwingAnimation

    上节博主介绍了AlphaAnimation和淡入淡出动画的使用,其实AlphaAnimation只是四种补间动画中的一种.那么为了加深对其他补间动画的理解,我想说说旋转动画RotateAnimatio ...

  6. Android开发笔记(十四)圆弧进度动画CircleAnimation

    一个好看的APP,都有不少精致的动画效果.熟练运用各种动画技术,可让我们的APP灼灼生辉.Android在技术上把动画分为了三类,分别是帧动画FrameAnimation.补间动画TweenAnima ...

  7. Android开发笔记(十二)测量尺寸与下拉刷新

    尺寸测量的配置 控件宽和高的设置方式 大家知道,自定义视图的目的就是要在屏幕上显示期望的图案,那在绘制图案之前,我们得先知道这个图案的尺寸(如宽多少高多少). 一般在xml中给控件的宽和高有三种赋值方 ...

  8. Android开发笔记(一百八十七)利用估值器实现弹幕动画

    如今上网看电影电视越发流行了,追剧的时候经常看到视频上方数行评论向左边飘去,犹如子弹那样飞快掠过,这些评论文字因此得名"弹幕".弹幕评论由正在观看的网友们即兴发表,故而连绵不绝从画 ...

  9. Android开发笔记(十)常用的图片加工操作

    APP开发自然少不了各种图片的加工处理,虽然说加工图片是美工干的活,但是码农会些简单的操作总归是好事.一些简单的加工如缩放.旋转.裁剪.格式转换,使用ACDSee就够用了,不过有些稍微复杂的操作得借助 ...

  10. Xamarin.Android开发实践(十八)

    Xamarin.Android之SlidingMenu 一.前言 有位网友在评论中希望能够出个在Xamarin.Android下实现SlidingMenu效果的随笔,刚好昨天在观看官网示例项目的时候也 ...

最新文章

  1. C 语言实例 - 计算自然数的和
  2. KMP子串匹配算法(Knuth–Morris–Pratt algorithm)
  3. 洛谷P2746 [USACO5.3]校园网Network of Schools
  4. 现在就开始使用AngularJS的三个重要原因
  5. SAP Hybris Enterprise Commerce Platform ECP和SAP CRM架构比较
  6. 设计模式 生成器_生成器设计模式的应用
  7. day20——安装客户端、数据库连接、mysql事务、mysql操作数据
  8. 神经网络自适应反馈控制设计
  9. Unreal 凹多边形三角化
  10. android qq 邮箱格式,qq邮箱怎么填写格式 qq邮箱格式写法介绍
  11. Hololens2开发-3-打包编译安装Hololens应用
  12. Electron-vueda打包EXE文件,打开后白屏
  13. 华为eNsp S5700组网配置
  14. DataSet 过滤方法
  15. 《魔兽世界插件》教程—21点扑克游戏 Blackjack
  16. 仿淘宝 RecycleView滑动效果 底部view联动
  17. 如何修改mysql占用的端口号_修改mysql端口号(mysql的端口号)
  18. 外卖:争夺饥饿用餐者的战争
  19. mysql sql语句 参数化_C#参数化执行SQL语句,防止破绽攻击本文以MySql为例【20151108非查询操作】_mysql...
  20. 微型计算机的特点及其主板构成,第1章 计算机基础知识教案

热门文章

  1. vscode取消底部横滚动条(自动换行)
  2. 关于联合体输出的问题(是否小端模式)
  3. 花书+吴恩达深度学习(十五)序列模型之循环神经网络 RNN
  4. 短信接口与短信平台收费标准----速码云仅供参考
  5. 自动化测试--封装getDriver的方法
  6. python交叉验证结合线性回归_Python数据分析-线性回归、逻辑回归
  7. Docker安装MySQL教程
  8. 最新有道翻译接口JS逆向分析
  9. Mybatis-plus实现动态表名查询
  10. 如何搭建VUE环境?