这类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. 频繁创建对象而没有释放






































苏公安网备 32011402010933号