您的位置:首页 > 移动开发 > Android开发

Android——Exception异常的正确打开方式

2018-07-19 09:35 155 查看

背景介绍

我们每天都需要与各种个样的异常打交到,但是我们对异常了解吗?对其处理方式正确吗?了解的话就算了,不了解的可以看看下面的内容。

打开Exception

Exception的分类

先来看看下面这张图: 
 
从图中可以看出: 
1. Error(错误)和Exception(异常)都继承自Throwable类,我们重点关注Exception; 
2. 异常类分为检查异常(直接继承自Exception,除RuntimeException)和可不检查异常(Error和继承自RuntimeException的);

throw和throws的区别

  • throw通常在代码片段中用于直接抛出异常。
[code]public void test(){
throw new ClassNotFoundException();
}
  • 1
  • 2
  • 3
  • throws用在方法签名上,可抛出多种异常
[code]public void test() throws IOException, ArrayIndexOutOfBoundsException{
do something...
}
  • 1
  • 2
  • 3

自定义异常

只需要继承Exception就可以了。

[code]public class CustomException extends Exception{

public CustomException(String exceptionInfo){
super(exceptionInfo);
}
}

//使用CustomExeption
public void test(){
throw new CustomException("抛出了自定义异常");
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

try-catch-finally

我们都很熟悉使用try-catch-finally去捕获异常,但现在值得思考一下我们之前写的try-catch-finally是否正确了。 
Code Clean 指出,try-catch-finally代码块在程序中定义了一个范围,我们应该让它的语意更明确,所以不应该把大段的代码放在其中,而应该抽离出来。来看看下面这个例子。

[code]//这个方法中只定义了try-catch,而真正的操作放到deletePagerAndAllReference()中进行
public void delete(){
try{
deletePagerAndAllReference();
} catch (Exception e){
Log.e(e);
}
}

public void deletePagerAndAllReference() throws Exception{
//do delete
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

需要注意,catch中的return语句会在finally执行完成后才会被执行。

关于方法返回null的讨论

Code Clean 中,鲍勃大叔严厉批评了return null 这种骇人听闻的做法,这让程序中充满了类似obj != null 的判断。他建议在可能返回null的地方使用抛出异常,或者直接返回一种特例情况。例如下面这样:

[code]List<Employee> employees = getEmployees();
if(employees != null){
// doSomething...
}
  • 1
  • 2
  • 3
  • 4

由于getEmployees()可能返回null值,所以我们不得不每次调用的时候都去检查是否为null,但如果做如下更改:

[code]public List<Employee> getEmployees(){
if(..there are no employees..){
return Collections.emptyList();
//没有数据返回一个空的List,调用时就不必去检查它是否为空了
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

或者像下面这样:

[code]public List<Employee> getEmployees(){
if(..there are no employees..){
throw new NullPointerException("嘿!List<Employee>不能为null,仔细检查下吧!");
//没有数据返回一个空的List,直接抛出异常,让调用者们知道,这个地方存在错误,不该让List<Employee>为null的。
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Android中处理未捕获异常,并上报异常信息

在我们的应用中,可能存在一些我们没有捕获的异常,对于这些异常,我们可以把它保存下来,然后进行分析。来看看怎么做。 
首先我们需要implements Thread.UncaughtExceptionHandler 实现自己的异常处理类,然后调用* Thread.setDefaultUncaughtExceptionHandler()* 方法把我们的异常处理器设置到系统中,这样有为捕获的异常出现时,就能被我们自己处理了。 
当然,有一个重要的方法需要重写uncaughtException() 。下面看看完整例子。

[code]public class CrashHandler implements UncaughtExceptionHandler {

private static final CrashHandler mInstance = new CrashHandler();
private UncaughtExceptionHandler mDefualtCrashHandler;
private Context mContext;

/**
* 防止被重复创建
*/
private CrashHandler() {}

public static CrashHandler getInstance() {
return mInstance;
}

public void init(Context context) {
mContext = context.getApplicationContext(); // 确保获得的是系统级的Context
mDefualtCrashHandler = Thread.getDefaultUncaughtExceptionHandler(); // 获取系统默认的异常处理器
Thread.setDefaultUncaughtExceptionHandler(this); // 把当前实例设置为系统默认异常处理器
}

/**
* 这个方法是我们重写的重点,当系统出现未捕获异常时,就会调用这个方法
*
* @param t 出现未捕获异常的线程
* @param e 未捕获的异常
*/
@Override
public void uncaughtException(Thread t, Throwable e) {
try {
saveExceptionToFile(e);
} catch (Exception ex) {
ex.printStackTrace();
}

if (mDefualtCrashHandler != null) {
//如果系统有默认异常处理就使用它处理
mDefualtCrashHandler.uncaughtException(t, e);
} else {
//否则我们自行结束程序
android.os.Process.killProcess(Process.myPid());
}

}

private void saveExceptionToFile(Throwable e) throws IOException{
if (FileUtils.ExistSDCard()){
long currentTime = System.currentTimeMillis();
String crashTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(currentTime);
File file = new File(FileUtils.getAppCrashDir()+"crash" + crashTime + ".txt");
file.createNewFile();
try{
PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(file)));
pw.println(crashTime);
printPhoneInfo(pw);
pw.println();
e.printStackTrace(pw); //输出错误信息
pw.close();
} catch (Exception ex){
ex.printStackTrace();
}
}
}

/**
* 输出手机信息
*/
private void printPhoneInfo(PrintWriter pw) throws PackageManager.NameNotFoundException {
PackageManager pm = mContext.getPackageManager();
PackageInfo pi = pm.getPackageInfo(mContext.getPackageName(), PackageManager.GET_ACTIVITIES);
pw.print("App version: ");
pw.print(pi.versionName);
pw.print("_");
pw.println(pi.versionCode);

//android版本号
pw.print("OS Version: ");
pw.print(Build.VERSION.RELEASE);
pw.print("_");
pw.println(Build.VERSION.SDK_INT);

//制造商
pw.print("Vendor: ");
pw.println(Build.MANUFACTURER);

//手机型号
pw.print("Model: ");
pw.println(Build.MODEL);

//cpu架构
pw.print("CPU ABI: ");
pw.println(Build.CPU_ABI);
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99

看看怎么使用。

[code]public class IceApplication extends MultiDexApplication {
private static Context context;
@Override
public void onCreate() {
super.onCreate();
context = this;
//初始化异常处理类,这样我们的异常类就生效了
CrashHandler.getInstance().init(context);
}

public static Context getAppContext(){
return context;
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

总结

现在我们对异常有了一定的了解,从现在开始,在编程过程中要开始注意对异常的处理艺术了。

阅读更多
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: