最近使用picasso遇到一个问题,picasso load在某些机子上load本地图片load不出来java.io.IOException: Cannot reset picasso官方也没有解决这个问题,只好自己写一个加载本地资源的库,也遇到了不少坑
Decode内存占用过大
这个问题还是由于自己对于BitmapFactory decode了解的不够仔细,先贴出原始代码吧
/**
* 根据imageview宽高 decode 图片
*/
public static Bitmap decodeStream(InputStream is, ImageView imageView) {
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inJustDecodeBounds = true;
//获取imageview想要显示的宽和高
ImageSize imageViewSize = getImageViewSize(imageView);
opts.inSampleSize = ImageUtils.calculateInSampleSize(opts,
imageViewSize.getWidth(), imageViewSize.getHeight());
opts.inJustDecodeBounds = false;
return BitmapFactory.decodeStream(is, null, opts);
}
这段代码貌似乍看没什么问题,考虑到了imageview加载过大图片的问题(ImageUtils.calculateInSampleSize),也用了inJustDecodeBounds属性,在native decode 不生成bitmap,获取bitmap宽和高,根据宽高压缩图片。
运行的时候发现,内存一个劲的上升,查了半天才发现,少写了一句decode...
/**
* 根据imageview宽高 decode 图片
*/
public static Bitmap decodeStream(InputStream is, ImageView imageView) {
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inJustDecodeBounds = true;
//这行代码,如果没有这行代码,native就不会decode流,其实很简单
//低下你调用什么decode在inJustDecodeBounds = true;下面你就要写一样的方法先在native decode bitmap获取宽高
BitmapFactory.decodeStream(is, null, opts);
//获取imageview想要显示的宽和高
ImageSize imageViewSize = getImageViewSize(imageView);
opts.inSampleSize = ImageUtils.calculateInSampleSize(opts,
imageViewSize.getWidth(), imageViewSize.getHeight());
opts.inJustDecodeBounds = false;
return BitmapFactory.decodeStream(is, null, opts);
}
Android: SkImageDecoder:: Factory returned null
解决了上面一个问题后,图片还是load不出来...也没有什么异常或者crash的日志,只看到logcat 里面解析图片的时候会报Android: SkImageDecoder:: Factory returned null 查了下说是 “第一次取图片尺寸的时候is这个InputStream被使用过了,再真正取图片的时候又使用了这个InputStream,此时流的起始位置已经被移动过了,需要调用is.reset()来重置,然后再decodeStream(imgInputStream, null, options)就没问题了。 ”
于是乎就将代码改为
/**
* 根据imageview宽高 decode 图片
*/
public static Bitmap decodeStream(InputStream is, ImageView imageView) {
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inJustDecodeBounds = true;
BitmapFactory.decodeStream(is, null, opts);
is.reset();//加入reset
ImageSize imageViewSize = getImageViewSize(imageView);
opts.inSampleSize = ImageUtils.calculateInSampleSize(opts,
imageViewSize.getWidth(), imageViewSize.getHeight());
opts.inJustDecodeBounds = false;
return BitmapFactory.decodeStream(is, null, opts);
}
java.io.IOException
加入reset之后又遇到IOException 查了些也没具体说为什么就改成下面的方法了,果然好了
/**
* 根据imageview宽高 decode 图片
*/
public static Bitmap decodeStream(InputStream is, ImageView imageView) {
Bitmap bitmap = null;
BufferedInputStream buffer = null;
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inJustDecodeBounds = true;
try {
int buffersize = 16 * 1024;
buffer = new BufferedInputStream(is, buffersize);
buffer.mark(16 * 1024);
BitmapFactory.decodeStream(buffer, null, opts);
buffer.reset();
//获取imageview想要显示的宽和高
ImageSize imageViewSize = getImageViewSize(imageView);
opts.inSampleSize = ImageUtils.calculateInSampleSize(opts,
imageViewSize.getWidth(), imageViewSize.getHeight());
opts.inJustDecodeBounds = false;
bitmap = BitmapFactory.decodeStream(buffer, null, opts);
} catch (Exception e) {
e.printStackTrace();
} finally {
ImageUtils.closeQuietly(buffer);
ImageUtils.closeQuietly(is);
}
return bitmap;
}
但是,第二天使用oppo R7的时候还是报哪个错误,load不出来图。逐改成,确实解决了问题,都能顺利的decode图正常显示了。
/**
* 根据imageview宽高 decode 图片
*/
public static Bitmap decodeStream(InputStream is, ImageView imageView) {
Bitmap bitmap = null;
BufferedInputStream bis = null;
ByteArrayOutputStream baos = null;
try {
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inJustDecodeBounds = true;
bis = new BufferedInputStream(is);
baos = new ByteArrayOutputStream();
byte buffer[] = new byte[1024];
int len;
while ((len = bis.read(buffer, 0, buffer.length)) > 0) {
baos.write(buffer, 0, len);
}
byte[] imageData = baos.toByteArray();
BitmapFactory.decodeByteArray(imageData, 0, imageData.length, opts);
//获取imageview想要显示的宽和高
ImageSize imageViewSize = getImageViewSize(imageView);
opts.inSampleSize = ImageUtils.calculateInSampleSize(opts,
imageViewSize.getWidth(), imageViewSize.getHeight());
opts.inJustDecodeBounds = false;
bitmap = BitmapFactory.decodeByteArray(imageData, 0, imageData.length, opts);
} catch (Exception e) {
e.printStackTrace();
} finally {
IOUtils.close(bis);
IOUtils.close(baos);
IOUtils.close(is);
}
return bitmap;
}
网上搜了好多也没找到具体原因,只能看源代码了:
if (!is.markSupported()) {
is = new BufferedInputStream(is, DECODE_BUFFER_SIZE);
}
// so we can call reset() if a given codec gives up after reading up to
// this many bytes. FIXME: need to find out from the codecs what this
// value should be.
is.mark(1024);
于是看明白了,第一次取图片尺寸的时候is这个InputStream被使用过了,再真正取图片的时候又使用了这个InputStream,此时流的起始位置已经被移动过了,需要调用is.reset()来重置,然后再decodeStream(imgInputStream, null, options)就没问题了。 但是注意一个问题,is.mark(1024)是SDK中写死的,如果图片的大小超过1024字节,第一次decode取尺寸之后调用is.reset()会抛出IOException
http://stackoverflow.com/questions/10240042/ioexception-cannot-load-file