当前位置: 首页 > news >正文

【Android 性能分析】延伸阅读:关于异常捕获

在这里插入图片描述

Android中的异常捕获

在程序开发中,“崩溃”是开发者最不愿意见到的场景——它会直接导致应用闪退,严重影响用户体验。

在Java和Android中,崩溃的本质是“未被捕获的异常”。

本文将从基础的try-catch语法讲起,详细介绍如何捕获各类常见异常(崩溃),并结合实际场景说明异常处理的最佳实践。

异常与崩溃的本质

在Java/Android中,异常(Exception) 是程序运行时出现的非预期错误(如空指针、数组越界等),而崩溃是“未被捕获的异常”导致的程序终止。

异常分为两大类:

  • Checked Exception(受检异常):编译期强制要求处理的异常(如IOException),不处理会编译报错。
  • Unchecked Exception(非受检异常):编译期不强制处理,运行时才可能抛出的异常(如NullPointerException),通常由逻辑错误导致,是崩溃的主要来源。

try-catch基础语法:异常捕获的核心

try-catch是Java中处理异常的核心语法,用于“尝试执行可能出错的代码,并在出错时捕获异常”。完整语法包括trycatchfinally三个部分,以及用于主动抛异常的throwthrows

1. 基础结构

try {// 可能抛出异常的代码(如调用一个可能出错的方法)riskyOperation();
} catch (ExceptionType1 e) {// 捕获并处理 ExceptionType1 类型的异常handleException1(e);
} catch (ExceptionType2 e) {// 捕获并处理 ExceptionType2 类型的异常handleException2(e);
} finally {// 无论是否发生异常,都会执行的代码(通常用于释放资源)releaseResources();
}
  • try:包裹可能抛出异常的代码。如果执行过程中抛出异常,会立即跳转到对应的catch块。
  • catch:指定要捕获的异常类型,只有当try块抛出的异常与catch声明的类型匹配时,才会执行该块。
  • finally:可选,用于执行“必须完成的操作”(如关闭文件、释放网络连接),无论try块是否抛出异常,它都会执行。

2. 主动抛出异常:throwthrows

  • throw:在方法内部主动抛出一个具体的异常对象(通常用于校验参数合法性)。

    public void setAge(int age) {if (age < 0 || age > 150) {// 主动抛出非法参数异常throw new IllegalArgumentException("年龄必须在0-150之间");}this.age = age;
    }
    
  • throws:在方法声明处标注该方法可能抛出的异常类型(用于Checked Exception,告知调用者需处理)。

    // 声明方法可能抛出 IOException(Checked Exception)
    public void readFile(String path) throws IOException {FileReader reader = new FileReader(path);// ...
    }
    

常见异常类型与捕获示例

实际开发中,不同场景会抛出不同类型的异常。以下是Android开发中高频出现的异常及其捕获方式。

1. 空指针异常(NullPointerException

场景:调用null对象的方法或访问其属性(如String s = null; s.length();)。
捕获示例

String text = null;
try {int length = text.length(); // 此处会抛出 NullPointerExceptionLog.d("TAG", "文本长度:" + length);
} catch (NullPointerException e) {// 处理空指针:记录日志 + 容错(如使用默认值)Log.e("TAG", "文本为空,无法获取长度", e); // 打印异常堆栈,方便调试int length = 0; // 容错:默认长度为0
}

预防建议:调用前校验对象是否为nullif (text != null) { ... })。

2. 类型转换异常(ClassCastException

场景:强制转换不兼容的类型(如Object obj = "hello"; Integer num = (Integer) obj;)。
捕获示例

Object data = getSomeData(); // 假设返回的数据类型不确定
try {String str = (String) data; // 可能抛出 ClassCastExceptionLog.d("TAG", "转换后的数据:" + str);
} catch (ClassCastException e) {Log.e("TAG", "数据类型不匹配,无法转换为String", e);// 容错:使用默认值或提示错误String str = "未知数据";
}

预防建议:转换前用instanceof判断类型(if (data instanceof String) { ... })。

3. 数组越界异常(IndexOutOfBoundsException

场景:访问数组/集合中不存在的索引(如int[] arr = new int[3]; int val = arr[5];)。
捕获示例

int[] numbers = {1, 2, 3};
int index = 5;
try {int value = numbers[index]; // 抛出 ArrayIndexOutOfBoundsExceptionLog.d("TAG", "索引" + index + "的值:" + value);
} catch (ArrayIndexOutOfBoundsException e) {Log.e("TAG", "数组索引越界,索引:" + index + ",数组长度:" + numbers.length, e);// 容错:使用默认值int value = -1;
}

预防建议:访问前校验索引范围(if (index >= 0 && index < numbers.length) { ... })。

4. 非法参数异常(IllegalArgumentException

场景:方法接收无效参数(如WindowManager设置宽高为0、传递负数给要求非负的参数)。
捕获示例(以WindowManager添加悬浮窗为例):

WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
View floatingView = new View(this);
int width = 0; // 无效参数:宽高不能为0
int height = 0;WindowManager.LayoutParams params = new WindowManager.LayoutParams(width, height, Build.VERSION.SDK_INT >= 26 ? TYPE_APPLICATION_OVERLAY : TYPE_PHONE,FLAG_NOT_TOUCH_MODAL,PixelFormat.TRANSLUCENT
);try {windowManager.addView(floatingView, params); // 抛出 IllegalArgumentException
} catch (IllegalArgumentException e) {Log.e("TAG", "添加悬浮窗失败:参数不合法", e);// 容错:使用默认宽高重试params.width = 300;params.height = 300;windowManager.addView(floatingView, params);
}

预防建议:调用方法前校验参数合法性(如宽高>0、权限是否已获取)。

5. 资源未找到异常(ResourceNotFoundException

场景:访问不存在的资源(如getResources().getDrawable(R.drawable.nonexistent))。
捕获示例

try {Drawable drawable = getResources().getDrawable(R.drawable.logo); // 资源不存在时抛出异常imageView.setImageDrawable(drawable);
} catch (ResourceNotFoundException e) {Log.e("TAG", "资源未找到:logo", e);// 容错:使用默认图片imageView.setImageResource(R.drawable.default_logo);
}

6. 网络异常(IOException与子类)

场景:网络请求失败(如无网络、连接超时),IOException是Checked Exception,需显式处理。
捕获示例

public void fetchData(String url) {try {URL requestUrl = new URL(url);HttpURLConnection connection = (HttpURLConnection) requestUrl.openConnection();connection.connect();// 读取网络数据...} catch (MalformedURLException e) { // URL格式错误(IOException子类)Log.e("TAG", "URL格式错误:" + url, e);} catch (IOException e) { // 网络连接/读取错误Log.e("TAG", "网络请求失败", e);// 提示用户检查网络Toast.makeText(this, "网络异常,请稍后重试", Toast.LENGTH_SHORT).show();}
}

7. 权限异常(SecurityException

场景:访问需要权限但未获取的功能(如未申请CAMERA权限时调用相机)。
捕获示例

try {// 尝试打开相机(需要CAMERA权限)Camera camera = Camera.open();
} catch (SecurityException e) {Log.e("TAG", "未获取相机权限", e);// 引导用户申请权限ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, 100);
}

总结

以下是Java(含Android)中常见异常类型的分类表格,按“标准Java异常”和“Android特有异常”划分,包含类型(受检/非受检)、父类、常见场景及说明:

异常类名类型(Checked/Unchecked)父类常见场景说明
一、标准Java异常
NullPointerExceptionUncheckedRuntimeException调用null对象的方法/属性(如String s = null; s.length();最常见的运行时异常,因对象未初始化导致
ClassCastExceptionUncheckedRuntimeException强制转换不兼容类型(如Object obj = "str"; Integer i = (Integer) obj;类型转换时类型不匹配
IndexOutOfBoundsExceptionUncheckedRuntimeException访问数组/集合的无效索引(如List.get(10)但列表长度为5)子类包括ArrayIndexOutOfBoundsException(数组)、StringIndexOutOfBoundsException(字符串)
IllegalArgumentExceptionUncheckedRuntimeException方法接收无效参数(如传递负数给要求非负的参数、宽高设为0)通常由参数校验失败触发(如if (age < 0) throw new ...
IllegalStateExceptionUncheckedRuntimeException对象状态不符合操作要求(如调用Thread.start()两次、未初始化就使用)强调“对象状态错误”而非参数错误
ArithmeticExceptionUncheckedRuntimeException算术错误(如整数除以0:int a = 1 / 0;仅针对运行时才能发现的算术问题
NumberFormatExceptionUncheckedIllegalArgumentException字符串转数字失败(如Integer.parseInt("abc")IllegalArgumentException的子类,专门处理格式错误
IOExceptionCheckedException输入/输出错误(如文件未找到、网络中断、流关闭后读写)受检异常,强制处理(如文件操作、网络请求)
FileNotFoundExceptionCheckedIOException访问不存在的文件(如new FileInputStream("nonexist.txt")IOException的子类,文件操作常见
SQLExceptionCheckedException数据库操作错误(如SQL语法错误、连接失败)数据库编程中常见,需显式处理
InterruptedExceptionCheckedException线程被中断(如Thread.sleep()时调用thread.interrupt()多线程中需处理线程中断逻辑
CloneNotSupportedExceptionCheckedException调用clone()但类未实现Cloneable接口克隆对象时的受检异常
二、Android特有异常
RemoteExceptionCheckedException跨进程通信(IPC)失败(如AIDL调用时目标进程崩溃、Binder断开)Android IPC核心异常,强制处理跨进程通信失败场景
ResourceNotFoundExceptionCheckedNotFoundException访问不存在的资源(如getResources().getDrawable(R.id.nonexist)资源ID错误或资源未打包时抛出
ActivityNotFoundExceptionCheckedNotFoundException启动Activity但未在Manifest注册(如startActivity(new Intent(this, Xxx.class))组件未注册或Intent匹配失败
SecurityExceptionUncheckedRuntimeException访问需要权限但未获取的功能(如未申请CAMERA时调用相机)权限相关错误,Android 6.0+动态权限未授权时常见
NetworkOnMainThreadExceptionUncheckedRuntimeException主线程执行网络请求(Android 3.0+禁止)强制要求网络操作在子线程执行
AndroidRuntimeExceptionUncheckedRuntimeExceptionAndroid运行时错误(如Surface创建失败、Parcel数据错误)子类包括BadTokenException(窗口令牌无效)、ServiceNotFoundException(服务未找到)等
ParseExceptionCheckedException数据解析错误(如SimpleDateFormat.parse("2023/13/01")Android中日期、JSON等解析失败时常见(继承自Java但高频用于Android)

try-catch高级用法

1. 多重catch块的顺序

try块可能抛出多种异常时,catch块需按“子类在前,父类在后”的顺序排列,否则子类异常会被父类异常捕获,导致子类catch块失效。

错误示例

try {// 可能抛出 NullPointerException 或 Exception
} catch (Exception e) { // 父类异常在前,会拦截所有子类异常Log.e("TAG", "捕获异常", e);
} catch (NullPointerException e) { // 此块永远不会执行Log.e("TAG", "捕获空指针", e);
}

正确示例

try {// 可能抛出 NullPointerException 或 Exception
} catch (NullPointerException e) { // 子类在前Log.e("TAG", "捕获空指针", e);
} catch (Exception e) { // 父类在后Log.e("TAG", "捕获其他异常", e);
}

2. try-with-resources:自动释放资源

对于实现了AutoCloseable接口的资源(如FileInputStreamHttpClient),可使用try-with-resources语法,无需手动在finally中关闭资源(会自动调用close()方法)。

示例:

// 自动关闭 FileInputStream(无需手动调用 close())
try (FileInputStream fis = new FileInputStream("data.txt")) {byte[] buffer = new byte[1024];fis.read(buffer);
} catch (IOException e) {Log.e("TAG", "文件读取失败", e);
}

3. 异常链:包装原始异常

当需要将低层次异常转换为业务异常时,可通过initCause或构造方法包装原始异常,保留完整堆栈信息。

示例:

public void processData() throws BusinessException {try {// 数据库操作,可能抛出 SQLExceptiondb.query("SELECT * FROM users");} catch (SQLException e) {// 包装为业务异常,保留原始异常信息BusinessException be = new BusinessException("数据查询失败");be.initCause(e); // 关联原始异常throw be;}
}

五、异常处理的最佳实践

  1. 优先预防,而非捕获
    异常捕获是“补救措施”,更优的方式是提前校验参数(如if (obj != null)),从源头避免异常。

  2. 不要捕获所有异常(尤其是Error
    Error(如OutOfMemoryErrorStackOverflowError)通常是系统级错误,无法通过捕获恢复,捕获它们会掩盖严重问题。

    // 错误示例:不要捕获所有异常
    try {// ...
    } catch (Throwable t) { // Throwable 包含 Exception 和 Error// 可能掩盖致命错误
    }
    
  3. 避免空的catch块
    空的catch块会导致异常被“吞噬”,难以定位问题。至少应记录日志:

    catch (NullPointerException e) {Log.e("TAG", "空指针异常", e); // 必须记录堆栈信息
    }
    
  4. finally块中避免抛异常
    如果finally块抛出异常,会覆盖trycatch块中的异常,导致原始异常丢失。

  5. 结合全局异常捕获
    在Android中,可通过Thread.setDefaultUncaughtExceptionHandler设置全局异常处理器,捕获未被局部try-catch处理的异常(如崩溃前记录日志):

    Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {Log.e("全局异常", "未捕获的异常", throwable);// 可选:重启应用或跳转错误页面
    });
    

总结

异常处理是保证应用稳定性的核心环节。try-catch语法为我们提供了捕获异常的工具,但真正的关键在于:理解不同异常的场景,优先预防,合理捕获,详细记录,妥善容错

通过本文的介绍,希望你能掌握从基础语法到复杂场景的异常处理技巧,让应用在面对错误时更“健壮”,给用户更流畅的体验。

在这里插入图片描述

http://www.dtcms.com/a/570259.html

相关文章:

  • 地方社区网站 备案十堰网站建设联系电话
  • 赣榆区城乡建设局网站网站优化工作
  • Python每日一练---第五天:轮转数组
  • 建设银行档案管理网站wordpress divi布局
  • p2p网站审批营销网站建设培训
  • 视频融合平台EasyCVR:打造智慧酒店一体化安防体系,筑牢安全管理防线
  • 能领免做卡的网站html5响应式设计公司网站模板整站html源码下载
  • 从需求到上线:体育比分系统完整开发流程详解
  • 微信二维码网站制作网站开发建设推荐
  • 如何编写VR大空间《时空探秘・恐龙纪元》剧本
  • 牙科医院网站建设方案全网霸屏推广系统
  • IPA 一键加密工具实战,用多工具组合把加固做成一次性与可复用的交付能力(IPA 一键加密/Ipa Guard CLI/成品加固)
  • 官方网站建设哪家公司好中国会议营销网站
  • 贵阳网站制作软件中国建设银行网站企业网银收费
  • kotlin常用语法点理解
  • STM32是什么?
  • 提高网站的访问速度网站后缀net
  • 安卓网站开发c 网站开发 书
  • 网站编辑 图片批量免费素材网站无版权
  • 给网站网站做优化重庆网站编辑职业学校
  • 【双机位A卷】华为OD笔试之【排序】双机位A-银行插队【Py/Java/C++/C/JS/Go六种语言】【欧弟算法】全网注释最详细分类最全的华子OD真题题解
  • 巴彦淖尔网站建设公司互联网服务公司有哪些
  • 国家建设部网站倪虹旅游公司的网站怎么做
  • 医院做网站备案都需要什么网站判断手机跳转代码
  • 荆门网站建设514885网站如何添加内容
  • 电子商务的网站建设分析建设银行东莞招聘网站
  • 杭州大型网站建设彩票网站建设 极云
  • Spring-cloud 主键loadbalance
  • 网页模板下载网站知乎教育机构网站建设方案书
  • 高端网站建设浩森宇特电气行业网站建设多少钱