Android 6.0设备PNG解码原生崩溃,设置inSampleSize缩放是否关联?
inSampleSize引发崩溃的问题解析 嘿,我来帮你捋清楚这个问题——设置BitmapFactory.Options.inSampleSize本身不会直接导致崩溃,但在Android 6.0(API 23)的特定场景下,确实会触发底层libpng的解码bug。从你给出的栈追踪来看,崩溃发生在sub_filter_2bpp_neon64这个libpng处理2位深度像素的方法里,属于PNG解码流程中的底层异常。
为什么会出现这个问题?
Android 6.0自带的libpng版本存在一个针对低深度PNG(比如2位每像素的格式)的解码缺陷。当你设置inSampleSize进行降采样时,Skia引擎会调整解码逻辑,跳过部分像素行/列,而这个操作刚好触发了libpng里原本隐藏的边界处理错误,最终导致崩溃。
可行的解决方案
我整理了几个能解决这个问题的办法,你可以根据自己的场景选择:
避开低深度PNG的
inSampleSize使用
先通过inJustDecodeBounds获取图片的位深度,如果是2bpp的PNG,就不要用inSampleSize,改成先解码完整Bitmap再缩放:BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(yourImagePath, options); // 检查位深度 int bitsPerPixel = options.outConfig.getBitsPerPixel(); if (bitsPerPixel == 2) { options.inJustDecodeBounds = false; // 先解码完整图 Bitmap fullBitmap = BitmapFactory.decodeFile(yourImagePath, options); // 再按需缩放 Bitmap scaledBitmap = Bitmap.createScaledBitmap(fullBitmap, targetWidth, targetHeight, true); fullBitmap.recycle(); // 及时释放内存 } else { // 正常使用inSampleSize降采样 options.inSampleSize = calculateInSampleSize(options, targetWidth, targetHeight); options.inJustDecodeBounds = false; Bitmap scaledBitmap = BitmapFactory.decodeFile(yourImagePath, options); }转换图片格式
把出问题的PNG转换成32位ARGB或者24位RGB的PNG,甚至直接转成JPG格式,这样就能避开libpng对低深度PNG的解码bug。改用第三方图片加载库
Glide、Picasso这类成熟的图片库,内部对不同Android版本的解码逻辑做了大量兼容处理,会自动规避系统底层的这类bug,同时还能更高效地处理图片缩放和内存管理,省心不少。
额外提醒
测试的时候记得多覆盖几款Android 6.0的设备,毕竟不同厂商的系统可能对libpng有定制,但原生6.0的这个bug是普遍存在的。另外,处理Bitmap后别忘了及时调用recycle()释放内存,避免额外的内存问题。
内容的提问来源于stack exchange,提问作者tactoth

