现象:在sdcard上存放一个特殊分辨率的JPEG图片,比如1x10000。然后使用android自带的图库应用程序去浏览该图片。使用图库浏览图片,会先生成缩略图供用户浏览。此时,图库应用程序会异常退出,如果可以看其log,会发现dalvik分配内存时发生内存溢出。但是如果你使用别的图片查看工具,不去生成缩略图,直接打开该图片,则不会发生异常。
分析:经过分析,发现google在做缩略图时,有一处错误。Google设定所有缩略图的分辨率都是96x96。以此为目标,然后根据原始图片的分辨率和目标图片来计算缩放因子。相关函数在文件ThumbnailUtils.java中,其路径为:
/framework/base/media/java/android/media/ThumbnailUtils.java
函数具体定义为:
/**
* Creates a centered bitmap of the desired size.
*
* @param source original bitmap source
* @param width targeted width
* @param height targeted height
* @param options options used during thumbnail extraction
*/
public static Bitmap extractThumbnail(
Bitmap source, int width, int height, int options) {
if (source == null) {
return null;
}
float scale;
if (source.getWidth() < source.getHeight()) {
scale = width / (float) source.getWidth();
} else {
scale = height / (float) source.getHeight();
}
Matrix matrix = new Matrix();
matrix.setScale(scale, scale);
Bitmap thumbnail = transform(matrix, source, width, height,
OPTIONS_SCALE_UP | options);
return thumbnail;
}
如果原始图片的分辨率为1600x1200,计算出来的scale为:
scale = 96 / 1200 = 0.08
所需要的memory大小为:1600*0.08 * 1200*0.08 * 2 = 24Kbyte
如果原始图片的分辨率为1x10000,计算出来的scale为:
Scale = 96 / 1 = 96
所需要的memory大小为:1*96 * 10000*96 * 2 = 175Mbyte,
而其本身所需要的空间为:1 * 10000 * 2 = 19Kbyte。
Dalvik为每个进程设置了允许申请的memory大小,默认为16M。175M的memory需要是任何系统都不能满足的。
总结:google在处理scale时,应该是只考虑了大图像缩小的情况,而没有考虑小图象放大的情况,特别是宽需要放大,而高需要缩小(或相反)。为了不让图库应用程序异常退出,可以在这里做一些限制,当其所需memory过大时,可以不去生成缩略图。