返回介绍

6.10 其他情况的异常

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

最后,是一些不太好归类的异常信息。这就像武侠小说中的独行大侠,无门无派但也不能小觑。

6.10.1 TimeoutException

异常中的关键字:

com.android.internal.BinderInternal$GcWatcher.finalize()timed out after 10 seconds

发生频率:★

GC回收超时会抛出该异常,注意重写finalize方法时不要有超时的操作。 [1]

6.10.2 JSON解析异常

异常中的关键字:

org.json.JSONException:No value for UserName at

org.json.JSONObject.get(JSONObject.java:354)at……

发生频率:★★★

在JSON解析中经常会遇到这种异常。

这是因为我们在解析JSON的时候,使用了getString("UserName")而不是optString("UserName"),如果UserName这个key在JSON字符串中不存在,前者会抛出上述异常,后者则会返回空。

类似地,还有getJsonArray方法,建议的解决方案是改用optJsonArray方法,才不会发生崩溃。

6.10.3 JSONArray在初始化时为空

异常中的关键字:

java.lang.NullPointerException at

org.json.JSONTokener.nextCleanInternal(JSONTokener.java:116)at

org.json.JSONTokener.nextValue(JSONTokener.java:94)t……

发生频率:★★★

我们知道JSONArray的初始化如下所示:

public void simulateJSONException() throws JSONException {
  String jsonString = "";
  JSONArray array = new JSONArray(jsonString);
  for (int i = 0; i < array.length(); i++) {
    JSONObject jsonObject = array.getJSONObject(i);
  }
}

当jsonString这个值为空时,就会报上述的异常信息。

6.10.4 第三方SDK抛出的Crash

在引入第三方SDK的同时,也会引入SDK导致的崩溃。举个例子:GoogleAnalytics,简称GA。Google提供的这个工具,很多公司用来搜集线上Crash。殊不知,有些手机只要启动这个记录Crash的功能,就会Crash,每天会有一两千个崩溃就是因为这个原因导致的,异常信息如下所示:

java.lang.RuntimeException: Package manager has died at 
android.app.ApplicationPackageManager.getPackageInfo(Application
PackageManager.java:82) at
com.google.analytics.tracking.android.StandardExceptionParser.setIn
cludedPackages(Unknown Source)

我也是在把线上Crash收集到自己的服务器后,才发现这个问题的。后来把GA的这个Crash发送功能禁用掉,就不再因此而崩溃了。

6.10.5 两个不同类型的View有相同的id

异常中的关键字:

java.lang.IllegalArgumentException:Wrong state class,expecting View State but received class android.widget.ScrollView$SavedState instead.This usually happens when two views of different type have the same id in the same hierarchy.This view's id is id/0xff0000.Make sure other views do not use the same id.

发生频率:★★★

异常信息中不一定每次都是ScrollView,也有TextView或者其他控件。

异常信息已经解释得很清楚了,在一个页面中,两个不同类型的View有相同的id,就会导致崩溃。

这个悲剧的发生,是Android系统的内部机制导致的。ViewPager中有两个页面,每个页面的layout布局文件中都有一个id名叫scroll_view的控件,那么当我们重写onSaveInstanceState这个方法的时候,如果要保存scroll_view的状态,比如scrollX和scrollY的值,那么在onRestoreInstanceState方法中恢复这两个值时,就会分不清楚究竟是哪一个。

Android官方建议最好保证每个View的id都是唯一的,或者至少在一个局部的layout文件中这么做,因为很显然,如果同一个layout文件中有两个id都是"android:id="@+id/button"的按钮,那么通过findViewById的时候只能找到前面的按钮,后面的那个就没机会被找到了,所以Android官方的说法是合理的。

此外,还应该加上特别重要的一条:当在Activity中,确定要保存/恢复一个View的状态的时候,一定要保证它们有唯一的id,因为Android内部用id作为保存、恢复状态时使用的key,否则就会发生一个覆盖另一个的悲剧。

6.10.6 LayoutInfiater.from().infiate()使用不当导致的崩溃

异常中的关键字:

No package identifier when getting value for resource number 0x00000001

发生频率:★★★

在程序中使用LayoutInfiater.from().infiate()语句时,必须写在具体的子类中,一定不能工作在父类或虚类里,如下所示:

View view = LayoutInfiater.from(mContext)
          .infiate(LAYOUT_ID, this, true);

这里有个this指针的问题,当initView方法让虚类调用时,这个this指向谁?是虚类自己还是子类?正是因为Android系统搞不清楚所以就崩溃了,另外这个infiate本身就有一定的特殊性,是不能随便乱用this的。我尝试过把BaseGuideView里的initView方法不写成虚方法,而是一个空的函数,但依旧报错。所以遇到这种情况,加载布局一定要由各个子View自行加载并初始化。 [2]

6.10.7 ViewGroup中的玄机

异常中的关键字:

java.lang.IllegalArgumentException:parameter must be a descendant of this view

发生频率:★★★

这个崩溃,是通过ViewGroup的offsetRectBetweenParentAndChild方法抛出来的。

offsetRectBetweenParentAndChild方法抛出来的。



void offsetRectBetweenParentAndChild(void descendant,
   Rect rect, boolean offsetFromChildToParent,
   boolean clipToBounds)

该方法就是用来计算父子重叠的区域。它是通过所给的descendant这个View逐级向上寻找Parent View,同时将Rect转换为同级坐标系来计算的。

在这个方法的末尾,如果最终找到的Parent View和当前View不一致,则会抛出这个异常。说白了,就是descendant参数必须是当前View的子孙。 [3]

那么什么时候descendant不是当前View的子孙呢?在UI调整的时候,会改变当前界面中拥有焦点的控件。我们应该实时确保这个控件是当前View的子孙,所以相应的解决方案也很简单,每次都重新设置一下焦点,让当前View始终获得焦点。与此同时,如果是ListView,还要清空ListView中其他控件抢到的焦点。

6.10.8 Monkey点击过快导致的崩溃

异常中的关键字:

java.lang.NullPointerException at

android.view.ViewRootImpl$ViewRootHandler.handleMessage(ViewRootImpl.java:3046)at……

发生频率:★★

有种Crash,只有执行Monkey脚本时才会抛出来。没办法,人手点的速度远不及Monkey点击的速度。这种Crash,我们就不深究了,只要确保手点的时候不崩溃即可。

有一种相对成熟的解决方案,那就是为每个点击事件加一个延迟函数,如下所示:

public void onClick(View v) {
  if(isWindowLocked())
    return;
  // 接下来的代码执行点击按钮后的逻辑



}

我们把isWindowLocked这个延迟方法写到BaseActivity中:

public Boolean isWindowLocked() {
  long current = SystemClock.elapsedRealtime();
  if (current - mLastOnClickTime > 500) {
    mLastOnClickTime = current;
    return false;
  }
  return true;
}

这样Monkey就不会跑那么快了。

代码中500的意思是延迟0.5秒。这取决于Monkey中事件的间隔时间,一般我们设置为0.5秒。

6.10.9 图片缩放很多倍

异常中的关键字:

java.lang.IllegalArgumentException:bitmap size exceeds 32bits

发生频率:★★★

当图片缩放了很多倍时,导致内存溢出,就会抛出这个异常,多发生在全屏显示一张图片的时候。

如下所示,postScale方法中的参数就是宽和高比例,要在这里增加try…catch…捕获这个异常。

// srcWindth和


srcHeight是缩放前



// tagetWidth和


targetHeight缩放后



Float scaleW = (fioat)targetWidth / (fioat)srcWidty;
Float scaleH = (fioat)targetHeight / (fioat)srcHeight;
Matrix matrix = new Matrix();
Matrix.postScale(scalew,scaleH);

6.10.10 图片宽高为0

异常中的关键字:

java.lang.IllegalArgumentException:width and height must be>0 at

android.graphics.Bitmap.nativeCreate(Native Method)

发生频率:★★

产生这个异常,通常是因为没有取到图片的宽和高,于是就返回默认值0了。

这是件很诡异的事情,因为任何一张图片都是有宽和高的,那么唯一的一种解释就是,没加载到这个图片(比如说缓冲数据被清空),或者提前调用了获取图片的宽和高的方法,这时候就得到0值了。

暂时还没有完美的解决方案,只能看到哪个页面有这样的异常信息,就加try…catch…语句防止获取图片宽高时出错。

6.10.11 不能重复添加组件

异常中的关键字:

View xxxx has already been added to the window manager

发生频率:★★★

这个异常发生在windowmanger.addView(view)这行代码中,意思大体是说这个view在Window Manager中已经存在,不能再添加相同的了。

通常的解决办法是在添加view时,捕获这个异常,但是并没有解决问题,想要添加的view并没有被加入到Window Manager中。

于是我们想到,先执行windowmanger.removeView(view),再执行addView方法,这样就不会出问题了。但是问题接踵而至,当Window Manager中并不存在这个view时,执行remove方法反而会抛出View not attached to window manager的异常信息。基于此,得到终极解决方案,如下所示:

try {
  windowmanager.removeView(view);
} catch(IllegalStateException ex) {
  e.printStackTrace();
}
try {
  windowmanager.addView(view);
} catch(IllegalStateException ex) {
  e.printStackTrace();
}

也就是说,即使removeView失败,也能继续执行接下来的addView操作。

[1] 关于这个异常的不完全诊断,请参见http://stackoverf iow.com/questions/24021609/how-to-handle-java-util-concurrent-timeoutexception-android-os-binderproxy-f in。

[2] 关于这个崩溃的详细信息,请参见http://blog.csdn.net/yanzi1225627/article/details/37338565。

[3] 关于这个崩溃的详细信息,请参见http://blog.sina.com.cn/s/blog_5704bfaf0102v3bn.html。

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

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

发布评论

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