文档开发者中心
我的

Android异常分类

1  Java语法相关的异常

这类Crash通常是和Java语法有关系。同样的错误,在Java项目中也屡见不鲜。所幸的是,这些纯语言相关的异常都有相应的解决方案。

1)       空指针 NullPointException

简单地说就是调用了未经初始化的对象或者是不存在的对象,大多数的Crash在异常信息中都带有NullPointException这样的关键字,散布在各个Activity和Adapter中,但问题的根因其实有很多是其它原因导致的,如窗体泄漏很多也表现为NullPointException。下面列举下集中常见的情形:

a.    方法需要对传入的参数判空后再使用;

b.    对于外部接口调用,需要确保返回值中不为空,甚至需要确保执行该接口不会抛出其他异常导致程序退出。

例如,页面跳转前后,跳转前没准备好数据,跳转到目标页面执行onCreate方法时解析传过来的Intent时,发现bundle这个字典中的某些数据为空,那么使用的时候就会出现崩溃了。

c.    在App中过多使用全局变量,一旦发生内存回收,这些全局变量会被设置为空,而App程序又没有考虑如何处理这种情况。

对于这类原因导致的Crash,要尽量避免使用全局变量,如果万不得已必须要使用全局变量,也要是全局变量支持序列化到本地的机制,一旦我们要使用全局变量而又发现其为空的时候,就从本地反序列化回来。全局变量这类问题多发生在把App切换到后台,过一段时间后在切换到前台,因为要执行所在页面的onResume 和 onCreate方法,如果这些方法中有全局变量并且被回收,那么会立刻就崩溃。此外,即使切换回前台不会崩溃,由于这个页面所使用的全局变量被回收了,那么在页面跳转时,还是会发生崩溃。。

2)       角标越界 StringIndex/ArrayIndex/IndexOutOfBoundsException

这类Crash也是由于程序的不严谨导致的,IndexOutOfBoundsException是基类。对于字符串截取时发生的越界,会抛出StringIndexOutOfBoundsException的异常信息;而对于数组越界,则会抛出ArrayIndexOutOfBoundsException。下面列举下几种常见的情形:

a.    在遍历一个数组/集合时,要预判数组/集合是否为空,长度是否大于0;

b.    在使用数组/集合中的元素时,要预判数组/集合长度是否有这么长;

c.    字符串也是一种数组,我们经常会使用subString(start,end)这样的函数,如果start或end超过了字符串的长度,就会崩溃。每次使用该函数时,都要判断字符串的长度。

d.    ListView操作不当也会导致IndexOutOfBoundsException的异常。

3)       试图调用一个空对象的方法 Attempt to invoke virtual method on a null object reference

这种Crash的产生,是因为在使用一个对象的某个方法时,这个对象为空,就是说没有实例化。下面列举下几种常见的情形:

a.    将实例化的语句写在条件语句(如if-else)的一个分支中,日常开发和测试工作只保证了带有实例化的情况,所以不会崩溃。实际场景中,没有实例化的分支会被大量的用户群所点到,于是就崩溃了;

b.    在一个Activity A中,调用另一个Activity B 的方法,为此在B中建立一个static变量,当这个static变量被回收时,就会有上述异常;

c.     还有就是推送,点击推送消息,根据事先定好的协议,跳过首页直接进入二级甚至三级页面,这时,二级页面要使用首页某个对象时,这个对象势必为空,那也会引发同样的异常。

4)       类型转换异常 ClassCastException:classA cannot be cast to classB

这类Crash都是由于强制类型转换导致的,如下所示:

Object x = new Integer(0);

String str = (String)x;

这就会抛出ClassCastException的异常了。

使用安全类型转换函数。在把字符串转换为整数、小数或布尔类型时,我们要为其指定转换失败时的默认值。否则,就会得到一个空值,放到哪里使用都崩溃。

5)       数字转换错误 NumberFormatException

在数据类型转换过程中,如果转换不成功,一般抛出ClassCastException的异常,只有一个情况,当字符型转换为数字失败时,Android系统会抛出NumberFormatException异常,如下所示:

String abc = “123xxx45”;

Int result = Integer.parseInt(abc);

这种情况多发生在服务器返回数据,没有按照约定返回整数而是字符串,客户端必须要事先考虑到这种情况,如果转换失败,必须有默认值而不是直接就崩溃了。

6)       声明数组时长度为-1 NegativeArraySizeException

数组大小为负值异常。当使用负数大小值创建数组时抛出该异常,在声明一个数组时,如果数组长度是由另一个变量动态得到的,要保证中括号[]中的值必须大于0。

7)       除数为0 java.lang.ArithmeticException:divide by zero

在程序中执行一个除法时,如果除数为0,就会发生上述崩溃,一般我们不会直接写出除数为0的异常来,这样的Crash多发生在第三方控件中。

比如说GifView,这个框架很有名,用于显示gif动画。GrifView这个开源项目有很多变体,但是无论如何,都应该注意其中movie的duration方法,这个值表示动画持续的时间,在接下来的代码中将会作为除数,如果为0,就会抛出上述的异常信息了,这时候要将其设置为默认值1秒。

8)       类找不到 ClassNotFoundException

当我们动态加载一个类的时候,如果这个类在运行时找不到,就会抛出这个异常。下面列举下几种常见的情形:

a.    如Class会有一个forName方法:Class.forName(“com.company.package.class”),由于类的全名称是字符串形式,这个值有可能是不正确的,那自然就会加载不成功了。类似的方法还有:ClassLoader中的findSystemClass(“classname”)方法。ClassLoader中的loadClass(“classname”)方法;

b.    当我们在B类中声明一个A类的实例,如下所示:ClassA obj = new ClassA(),但是打包时B和A分别位于不同的dex中,这时如果在A所在的dex中把A类删除了,那么在运行时执行到这句话时就会抛出NoClassDefFoundError的异常信息。


2      Activity相关的异常

Android四大组件,对于应用类App而言,使用最多的是Activity,APP中关于Activity的崩溃也是最多的。

1)       找不到Activity  android.content.ActivityNotFoundException

下面列举下几种常见的情形:

a.    URL不是以http开头,代码就会抛出异常:

Uri uri = Uri.parse(“www.baidu.com”);

Intent intent = new Intent(Intent.ACTION_VIEW, uri);

startActivity(intent);

b.    拉起第三方APP,用户手机没有安装第三方客户端;

c.    在AndroidManifest.xml文件中没有注册;

2)       不能实例化Activity  java.lang.RuntimeException:Unable to instantitate activity ComponentInfo

这种Crash,通常是因为没有在AndroidManifest.xml文件中注册该activity,或者在创建完activity后,修改了包名或者activity的类名,而配置清单中没有修改,造成不能实例化。也可能是系统处于异常状态(关机,内存不足)等,导致部件初始化失败。

3)       猴急的Fragment  Fragment not attached Activity

Fragment在还没有Attach到Activity时,调用了诸如getResource()这样的方法。相应的解决方案是,在获取资源前先使用isAdded方法进行判断,如下:

if(isAdded())

{

      getResources().getString(R.string.app_name);

}

      isAdd方法是Android系统提供的,它只有在Fragment被添加到所属的Activity后才会返回true。


3      序列化相关的异常

1)    实体对象不支持序列化 Parcelable encountered IOException writing serializable object

如JSONObject和JSONArray不支持序列化,对于JSONObject和JSONArray这样的类型,也是不支持序列化的,所以实体中一旦有这样的属性,必然崩溃。

2)    序列化时未指定ClassLoader BadParcelableException:ClassNotFoundException when Unmarshalling

当ClassLoader为空时,系统会采取默认的ClassLoader。Android有两种不同的ClassLoader: framework ClassLoader 和 apk ClassLoader,其中framework ClassLoader 知道怎么加载Android系统内部的类;apk ClassLoader知道怎么加载我们自己写的类,也知道怎么加载Android系统内部的类。

3)    反序列化时发现类找不到 Parcelable encountered ClassNotFoundException reading a Serializable object

对于在反序列化的时候用到Class.forName(className)方法时,一定要注意了,记得在ProGuard文件中keep这个类。

-keep public class com.example.demo.MyParcelable


4      窗口相关的异常

1)    窗体句柄泄露 WindowManager: android.view.WindowLeaked

举例如下:

以上代码运行时必然崩溃,这是因为,最后finish语句销毁了当前Activity,但是在它基础上创建的AlertDialog对话框还在,窗口句柄泄露,未能及时销毁。

2)    View not attached to window manager

要想避免此类Exception,就要正确的使用对话框,也要正确的使用线程,有以下几点需要注意:

a. 正确使用对话框。不要在非UI线程中使用对话框创建,显示和取消对话框。

b. 一定要让对话框对象在Activity的可控制范围内和生命周期之内。比如对话框一定要是Activity的成员变量,并且在让对话框变量活跃在Activity的onCreate()和onDestroy()这两个方法之间。


5      资源相关的异常

1)    UnsatisfiedLinkError

出现这个异常,一般都是so格式的文件有关。在异常信息中就可以找到错误的原因。例如:

a.    java.lang.UnsatisfiedLinkError:dalvik.system.PathClassLoader[DexPathList[[zip file 异常信息后面跟的是路径的话,就说明是因为so文件找不到造成的,这个时候我们就要检查so文件是否已经放入到了正确的路径下。

b.    异常信息后面跟的是方法名的话,so文件加载成功了,但是找不到相应的方法,这个时候就要检查So文件的版本是否正确,以及你调用so中方法处方法名是否书写正确。


6      内存溢出相关的异常

1)    OutOfMemoryException

a.    使用Adapter时,没有复用convertview;

b.    使用非静态内部类;

c.    单例模式时需要context参数时,使用Activity的引用;

d.    注册对象时,没有进行反注册;

e.    使用资源对象时,没有关闭;

f.     使用Bitmap后没有用recycle释放;

g.    集合中的对象没有清理;

h.    频繁创建对象而没有释放


点赞
5
收藏
2
回复
4
分享
举报
浏览18757 编辑于2019-07-12 08:12未知归属地
全部评论
最多点赞
最新发布
最早发布
楼主辛苦了,整的不赖
4楼回复于2023-08-09 02:47 来自河南
学到了
3楼回复于2022-11-13 07:14 来自广东
学到了
2楼回复于2022-11-08 04:51 来自广东

总结的不错!

1楼回复于2019-07-12 09:09 未知归属地
写回答
新增插入模板功能
一键使用模板,快速填写内容,轻松发帖~
知道了
  • 为了保障您的信息安全,请勿上传您的敏感个人信息(如您的密码等信息)和您的敏感资产信息(如关键源代码、签名私钥、调试安装包、业务日志等信息),且您需自行承担由此产生的信息泄露等安全风险。
  • 如您发布的内容为转载内容,请注明内容来源。

我要发帖子

了解社区公约,与您携手共创和谐专业的开发者社区。