Android 异常容错处理
发布日期:2021-11-12 07:57:34 浏览次数:29 分类:技术文章

本文共 6284 字,大约阅读时间需要 20 分钟。

原文地址:http://blog.csdn.net/aqi00/article/details/50855205

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的示例代码:
[java]
  1. import java.io.PrintWriter;  
  2. import java.io.StringWriter;  
  3. import java.io.Writer;  
  4. import java.lang.Thread.UncaughtExceptionHandler;  
  5. import java.lang.reflect.Field;  
  6.   
  7. import com.example.exmexception.except.util.PropertiesUtil;  
  8. import com.example.exmexception.except.util.Utils;  
  9.   
  10. import android.content.Context;  
  11. import android.content.pm.PackageInfo;  
  12. import android.content.pm.PackageManager;  
  13. import android.content.pm.PackageManager.NameNotFoundException;  
  14. import android.os.Build;  
  15. import android.os.Looper;  
  16. import android.os.Process;  
  17. import android.util.Log;  
  18. import android.widget.Toast;  
  19.   
  20. public class CrashHandler implements UncaughtExceptionHandler {  
  21.         
  22.     public static final String TAG = "CrashHandler";  
  23.     //是否开启日志输出,在Debug状态下开启  
  24.     public static final boolean DEBUG = true;  
  25.     //系统默认的UncaughtException处理类  
  26.     private Thread.UncaughtExceptionHandler mDefaultHandler;  
  27.     //程序的Context对象  
  28.     private Context mContext;  
  29.   
  30.     private static final String VERSION_NAME = "versionName";  
  31.     private static final String VERSION_CODE = "versionCode";  
  32.     private static final String STACK_TRACE = "STACK_TRACE";  
  33.     private PropertiesUtil mProp;  
  34.   
  35.     //CrashHandler实例  
  36.     private static CrashHandler INSTANCE;  
  37.     //获取CrashHandler实例,单例模式  
  38.     public static CrashHandler getInstance() {  
  39.         if (INSTANCE == null) {  
  40.             INSTANCE = new CrashHandler();  
  41.         }  
  42.         return INSTANCE;  
  43.     }  
  44.          
  45.     //获取系统默认的UncaughtException处理器,设置该CrashHandler为程序的默认处理器   
  46.     public void setCrashHandler(Context ctx) {  
  47.         Log.d(TAG, "CrashHandler setCrashHandler");  
  48.         mContext = ctx;  
  49.         mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();  
  50.         Thread.setDefaultUncaughtExceptionHandler(this);  
  51.         mProp = PropertiesUtil.getInstance(ctx);  
  52.     }  
  53.          
  54.     //当UncaughtException发生时会转入该函数来处理  
  55.     @Override      
  56.     public void uncaughtException(Thread thread, Throwable ex) {  
  57.         Log.d(TAG, "CrashHandler uncaughtException");  
  58.         if (DEBUG) {  
  59.             ex.printStackTrace();  
  60.         }  
  61.         if (!handleException(ex) && mDefaultHandler != null) {  
  62.             Log.d(TAG, "mDefaultHandler.uncaughtException");  
  63.             //如果用户没有处理则让系统默认的异常处理器来处理      
  64.             mDefaultHandler.uncaughtException(thread, ex);  
  65.         } else {  
  66.             Log.d(TAG, "sleep and killProcess");  
  67.             try {  
  68.                 Thread.sleep(2000);  
  69.             }catch (InterruptedException e) {  
  70.                 Log.e(TAG, "Error : ", e);  
  71.             }  
  72.             Process.killProcess(Process.myPid());  
  73.             System.exit(10);  
  74.         }  
  75.     }  
  76.       
  77.     private String getMsg(Throwable ex) {  
  78.         //若是空指针异常,getLocalizedMessage返回的是null  
  79.         String msg = ex.getLocalizedMessage();  
  80.         if (msg == null) {  
  81. //          PrintStream err_msg = System.err.append(toString());  
  82. //          msg = err_msg.toString();  
  83.             StackTraceElement[] stackArray = ex.getStackTrace();  
  84.             StackTraceElement element = stackArray[0];  
  85.             msg = element.toString();  
  86.         }  
  87.         return msg;  
  88.     }  
  89.          
  90.     //自定义错误处理、收集错误信息、发送错误报告等操作均在此完成  
  91.     private boolean handleException(Throwable ex) {  
  92.         if (ex == null) {  
  93.             Log.d(TAG, "handleException --- ex==null");  
  94.             return true;  
  95.         }  
  96.         final String msg = getMsg(ex);  
  97.         if(msg == null) {  
  98.             Log.d(TAG, "getMessage is null");  
  99.             return false;  
  100.         }  
  101.         new Thread() {  
  102.             @Override      
  103.             public void run() {  
  104.                 Looper.prepare();  
  105.                 Toast.makeText(mContext, "程序出错,即将退出:\n"+msg, Toast.LENGTH_LONG).show();  
  106.                 Looper.loop();  
  107.             }  
  108.         }.start();  
  109.         String file_name = String.format("crash-%s.log", Utils.getNowDateTime());  
  110.         mProp.setFile(file_name).init();  
  111.         //收集设备信息      
  112.         collectCrashDeviceInfo(mContext);  
  113.         //保存错误报告文件      
  114.         saveCrashInfoToFile(ex);  
  115.         //保存错误信息  
  116.         mProp.commit();  
  117.         //发送错误报告到服务器,若后台需要获取错误报告则打开  
  118.         //sendCrashReportsToServer(mContext);  
  119.         return true;  
  120.     }  
  121.          
  122.     //保存错误信息到文件中   
  123.     private void saveCrashInfoToFile(Throwable ex) {  
  124.         Log.d(TAG, "saveCrashInfoToFile");  
  125.         Writer info = new StringWriter();  
  126.         PrintWriter printWriter = new PrintWriter(info);  
  127.         ex.printStackTrace(printWriter);  
  128.         Throwable cause = ex.getCause();  
  129.         while (cause != null) {  
  130.             cause.printStackTrace(printWriter);  
  131.             cause = cause.getCause();  
  132.         }  
  133.         String result = info.toString();  
  134.         printWriter.close();  
  135.         mProp.writeString("EXEPTION", getMsg(ex));  
  136.         mProp.writeString(STACK_TRACE, result);  
  137.     }  
  138.     
  139.     //收集程序崩溃的设备信息   
  140.     private void collectCrashDeviceInfo(Context ctx) {  
  141.         Log.d(TAG, "collectCrashDeviceInfo");  
  142.         try {  
  143.             PackageManager pm = ctx.getPackageManager();  
  144.             PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), PackageManager.GET_ACTIVITIES);  
  145.             if (pi != null) {  
  146.                 mProp.writeString(VERSION_NAME, (pi.versionName==null)?"not set":pi.versionName);  
  147.                 mProp.writeInt(VERSION_CODE, pi.versionCode);  
  148.             }  
  149.         }catch (NameNotFoundException e) {  
  150.             Log.e(TAG, "Error while collect package info", e);  
  151.         }  
  152.         //使用反射来收集设备信息,例如:系统版本号、设备生产商等环境信息  
  153.         Field[] fields = Build.class.getDeclaredFields();  
  154.         for (Field field : fields) {  
  155.             try {  
  156.                 field.setAccessible(true);  
  157.                 mProp.writeString(field.getName(), ""+field.get(null));  
  158.                 if (DEBUG) {  
  159.                     Log.d(TAG, field.getName() + " : " + field.get(null));  
  160.                 }  
  161.             }catch (Exception e) {  
  162.                 Log.e(TAG, "Error while collect crash info", e);  
  163.             }  
  164.         }  
  165.     }  
  166.     
  167. }  
下面是Application类中的调用代码:
[java]
  1. import com.example.exmexception.except.CrashHandler;  
  2.   
  3. import android.app.Application;  
  4.   
  5. public class MainApplication extends Application {  
  6.       
  7.     @Override  
  8.     public void onCreate() {  
  9.         super.onCreate();  
  10.         // 注册crashHandler  
  11.         CrashHandler crashHandler = CrashHandler.getInstance();  
  12.         crashHandler.setCrashHandler(getApplicationContext());  
  13.     }  
  14.       

转载地址:https://blog.csdn.net/happy_love1990/article/details/77008055 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:Android 同步与加锁
下一篇:ToolBar的使用

发表评论

最新留言

路过按个爪印,很不错,赞一个!
[***.219.124.196]2024年03月26日 21时46分07秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章