前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android开发笔记(七十八)异常容错处理

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

作者头像
aqi00
发布2019-01-18 13:01:08
8210
发布2019-01-18 13:01:08
举报
文章被收录于专栏:老欧说安卓

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的示例代码:

代码语言:javascript
复制
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返回的是null
    	String 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类中的调用代码:

代码语言:javascript
复制
import com.example.exmexception.except.CrashHandler;

import android.app.Application;

public class MainApplication extends Application {
	
	@Override
	public void onCreate() {
		super.onCreate();
		// 注册crashHandler
		CrashHandler crashHandler = CrashHandler.getInstance();
		crashHandler.setCrashHandler(getApplicationContext());
	}
	
}

点击下载本文用到的异常容错处理的工程代码 点此查看Android开发笔记的完整目录

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2016年03月11日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Exception
  • CrashHandler
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档