1. 背景
今天在测试应用的时候出现一个问题,分析之后,我觉得特别奇怪,可以看下面的报错:
E/AndroidRuntime(29778): java.lang.StackOverflowError
E/AndroidRuntime(29778): at android.graphics.drawable.BitmapDrawable.computeBitmapSize(BitmapDrawable.java:189)
E/AndroidRuntime(29778): at android.graphics.drawable.BitmapDrawable.setBitmap(BitmapDrawable.java:197)
E/AndroidRuntime(29778): at android.graphics.drawable.BitmapDrawable.<init>(BitmapDrawable.java:689)
E/AndroidRuntime(29778): at android.graphics.drawable.BitmapDrawable.<init>(BitmapDrawable.java:62)
E/AndroidRuntime(29778): at android.graphics.drawable.BitmapDrawable$BitmapState.newDrawable(BitmapDrawable.java:673)
E/AndroidRuntime(29778): at android.content.res.Resources.getCachedDrawable(Resources.java:2183)
E/AndroidRuntime(29778): at android.content.res.Resources.loadDrawable(Resources.java:2048)
E/AndroidRuntime(29778): at android.content.res.Resources.getDrawable(Resources.java:700)
E/AndroidRuntime(29778): at androidx.appcompat.widget.ResourcesWrapper.getDrawable(ResourcesWrapper.java:130)
E/AndroidRuntime(29778): at androidx.appcompat.widget.TintResources.getDrawable(TintResources.java:46)
E/AndroidRuntime(29778): at androidx.core.content.ContextCompat.getDrawable(ContextCompat.java:456)
E/AndroidRuntime(29778): at androidx.appcompat.widget.ResourceManagerInternal.getDrawable(ResourceManagerInternal.java:144)
E/AndroidRuntime(29778): at androidx.appcompat.widget.ResourceManagerInternal.getDrawable(ResourceManagerInternal.java:132)
E/AndroidRuntime(29778): at androidx.appcompat.content.res.AppCompatResources.getDrawable(AppCompatResources.java:104)
E/AndroidRuntime(29778): at androidx.appcompat.widget.AppCompatImageHelper.setImageResource(AppCompatImageHelper.java:86)
E/AndroidRuntime(29778): at androidx.appcompat.widget.AppCompatImageView.setImageResource(AppCompatImageView.java:94)
E/AndroidRuntime(29778): at plat.skytv.main.activity.CpappDetailActivity.updateUiAsVip(CpappDetailActivity.java:705)
2. 分析
2.1 首先 我的疑问在 仅仅 调用了 一次 ImageView.setImageResource(int resourceid); 怎么就蹦了了,而且不是OOM,而是堆栈溢出。
2.2 其次 这个不是系统的方法嘛,难道调用系统的方法也不行了嘛,为此我还想过 使用Glide去加载图片。
2.3 最后看了一篇文章,虽然 发生错误的原因不同,但是结果却是相同的,让我有了一点思路。
2.4 检查代码逻辑之后,发现是 两个方法递归调用,虽然里面并没有复杂的逻辑,但是却一直在调用 ImageView.setImageResource。
2.5 然后我又去看了下 StackOverflowError 和 OutOfMemoryError 的区别:
StackOverflowError :是Java线程操作是基于栈的,如果两个方法循环调用,就会一直进栈而不会出栈,如果栈的深度超过了虚拟机所允许的深度,就会抛出这个问题。
OutOfMemoryError:栈的深度是可以自动扩展的,但是如果扩展时无法申请到足够的内存时就会OOM。
3. 解答
3.1 因为我的问题是android 4.4 的盒子上出现的,就去看了 4.4 的源码,然后看到了这么一句:
public void setImageResource(int resId) {
...
resolveUri();
...
}
}
然后 看一下 这个 resolveUri 方法主要干了啥:
private void resolveUri() {
...
d = rsrc.getDrawable(mResource);
...
}
然后一路跟进去会到 loadDrawable 这个方法:
Drawable loadDrawable(TypedValue value, int id)
throws NotFoundException {
...
Drawable dr = getCachedDrawable(isColorDrawable ? mColorDrawableCache : mDrawableCache, key);
if (dr != null) {
return dr;
}
...
return dr;
}
看到这我就明白了,代码在循环调用的时候,应该是从缓存中去取了已经加载好的图片,不会去创建新的Bitmap,所以并没有OOM的问题。
4. 总结
4.1 如果发生 StackOverflowError 的问题,在问题代码前后需要看下时候有递归调用,这个很重要。
4.2 记得 分析日志,android系统已经是一个相对比较完善的生态,必然不会无缘无故的报错崩溃,在崩溃前后肯定有一些蛛丝马迹,善于分析,然后网上查找资料,扩充知识库,不仅找你发生的问题,最好源码也分析一遍,加深印象。
4.3 参考:
https://www.kanzhun.com/mianshiti/496991.html
https://blog.csdn.net/u012410733/article/details/53464295
https://blog.csdn.net/Icannotdebug/article/details/78844619