返回介绍

6.6 资源相关的异常

发布于 2024-08-17 23:46:12 字数 5430 浏览 0 评论 0 收藏 0

资源相关的异常,基本都容易解决。但是有一种情况非常恶心,就是明明apk包中有这个资源文件,但是仍然抛出该资源找不到的异常,对此我们也只好认为是内存溢出(OOM)了。

6.6.1 Resources$NotFoundException

异常中的关键字:

android.content.res.Resources$NotFoundException:String resource ID#0x1

发生频率:★★★

这种异常一般是因为参数int resId错误,我们把String赋值给int的resId,所以编译器找不到正确的resource而报错。

最简单的例子,检查一下项目中以下语句的使用:

Toast.makeText();
textView1.setText();

类似还有一些,这里不列举出来了。这样的函数通常有几个重载,如TextView的重载函数如下:

TextView.setText(CharSequence text);
TextView.setText(int resId);

如果不小心将一个int值传给了它,那它不会显示该int值,而是跑到工程下去找一个对应的resource的id,那当然是找不到的,于是就报错了。

比如我这里是这样的:

count.setText(incall.getCount());

incall.getCount();返回的是一个int值,直接执行setText方法是肯定不行的,就会发生上述的Crash。

解决办法如下:

count.setText(String.valueOf(incall.getCount()));

或者

count.setText(incall.getCount() + "");

6.6.2 StackOverfiowError

异常中的关键字:

StackOverfiowError

发生频率:★★★

发生这种事情,主要是因为Layout布局文件结构嵌套层次太深。我们应尽量控制在5层以下。要经常使用Hierarchy View对其进行优化,移除不必要的视图。

产生这种Crash的第二种原因是,在App退出的时候,如果App中有多个线程,那么在退出App的时候可能不能完全关闭App,即使使用finish方法也做不到,必须使用System.exit(0)这样的语句才可以。

这是因为finish方法只能退出当前Activity,但还可能有其他Activity未关闭,这些Activity中有没结束的线程,从而会有一些资源没有释放。

而exit(int code)方法可以使进程退出能保证把所有线程的栈空间释放,否则残留的线程栈空间无法回收,将会导致该进程新建线程时栈空间不足,而发生StackOverfiowError的异常。

无论是哪种情况导致的StackOverFlowError,都是由无限递归引起的。在JVM中有一个栈,预设了一个深度,当超出这个深度时,就会抛出StackOverFlowError。我们上述种种解决方案,都是在避免无限循环调用。

6.6.3 UnsatisfiedLinkError

异常中的关键字:

java.lang.UnsatisfiedLinkError:dalvik.system.PathClassLoader[DexPathList[[zip file"/data/app/appname-1.apk"]……."

发生频率:★★★★★

遇到这个Crash,肯定是so格式的文件没有加载到。检查libs的armeabi目录下的so文件是否存在。

此外,不能只看armeabi下是否有so文件,还要看x86目录下so文件是否存在,如果没有,在x86的设备上仍然是加载不到。

由此而上升到CPU指令集,Android上一共有4种,armeabi、armeabi-v7a、mips和x86。处理so文件时要格外小心。 [1]

如图6-3所示,armeabi和armeabi-v7a的so数量不一致,是典型的会导致UnsatisfiedLinkError的场景。

图6-3 某App下的so文件

6.6.4 InfiateException之FileNotFoundException

异常中的关键字:

Caused by:android.view.InfiateException:Binary XML file line#18:Error infiating class<unknown>at android.view.LayoutInfiater.createView(LayoutInfiater.java:518)

Caused by:java.io.FileNotFoundException:res/drawable-hdpi/add.png at android.content.res.AssetManager.openNonAssetNative(Native Method)

发生频率:★★★★★

咋一看,还以为是资源找不到,于是去相应的drawable目录下去寻找,就发现这个add.png文件确实存在啊。

我在网上找了好久好久关于这个Crash的描述,但大都不满意。目前看到的一种比较靠谱的说法是GC导致的。Activity销毁了,但是里面涉及的资源并没有被回收,于是便产生内存泄露了,但是表现为FileNotFoundException。

对此,相应的解决方案是,在Activity的onStop方法中,手动释放每一张图片资源。 [2]

6.6.5 InfiateException之缺少构造器

异常中的关键字:

android.view.InfiateException:Binary XML file line#:Error infiating class com.example.activity1.TestButton

发生频率:★★★

创建自定义view的时候,碰到上述这个异常,反复研究后发现是缺少一个构造器造成。其中第二个参数用来将xml文件中的属性初始化。

自定义控件若需要在xml文件中使用,就必须重写带如上两个参数的构造方法。添加后即可正常使用了:

public MyView(Context context, AttributeSet paramAttributeSet) {
  super(context, paramAttributeSet);
}

补齐这个构造器,异常就消失了。

6.6.6 InfiateException之style与android:textStyle的区别

异常中的关键字:

android.view.InfiateException:Binary XML file line#14:Error infiating class

发生频率:★★

在一个xml布局文件中,对于实现已经定义好的样式:

<style name="NormalText">
  <item name="android:textSize">14sp</item>
  <item name="android:textStyle">normal</item>
  <item name="android:textColor">@color/Gray1</item>
</style>

去引用:

<TextView
  android:id="@+id/tvUserName"
  android:text="@string/hello_world"
  android:layout_width="230dp"
  android:layout_height="30dp"
  android:layout_marginLeft="10dp"
  android:layout_marginTop="10dp"
  android:textStyle="@style/NormalText"
  />

结果发现运行时出错,抛出android.view.InfiateException:Binary XML file line#14异常。

按图索骥,我们找到资源文件的第14行,发现是style的问题,后来去参考Android官方文档,感觉应该是把:

android:textStyle="@style/NormalText"

改为:

style="@style/NormalText"

修改后的布局文件如下所示:

<TextView
  android:id="@+id/tvUserName"
  android:text="@string/hello_world"
  android:layout_width="230dp"
  android:layout_height="30dp"
  android:layout_marginLeft="10dp"
  android:layout_marginTop="10dp"
  />

6.6.7 TransactionTooLargeException

异常中的关键字:

android.view.InfiateException:Binary XML file line#14:Error infiating class

发生频率:★★★

官方文档里的解释是,Binder最大通常限制为1MB,如果大于1MB的话,就会抛出TransactionTooLargeException的异常。

相应的解决方法是:不要将大量数据传入Binder,比如说图片。

这个Crash经常出现在图片分享的功能中,因为我们要给第三方分享SDK传递很大的图片。此外,使用采集打点数据时也会看到这类Crash,因为打点的机制不是每点击一次按钮就发一次,而是数据积累到一定量后再发,这个阀值太大就会导致抛出TransactionTooLargeException异常。

[1] 详细情况请参见“Androidndk开发打包时我们应该如何注意平台的兼容”,文章地址:http://www.cnblogs.com/devinzhang/archive/2012/02/29/2373729.html。

[2] 关于这个Crash的详细描述,请参见http://blog.csdn.net/yiding_he/article/details/38597703。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文