<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">项目中要加载从网络上获取的图片资源,但是有的图片很大,可以达到1080*1920的尺寸,这么大的图片如果全部载入内存,很容易出现Out Of Memory(OOM)的问题。</span>
解决的方法是利用BitmapFactory.Options。
HttpURLConnection hp = (HttpURLConnection) imageURL.openConnection();
InputStream is = hp.getInputStream();
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
//inJustDecodeBounds设置为true后,decodeStream返回为null,但是options中存储了图片的尺寸信息
BitmapFactory.decodeStream(is, null, options);
calcuteInSampleSize(options, reqWidth, reqHight);
options.inJustDecodeBounds = false;
bitmap = BitmapFactory.decodeStream(is, null, options);
calcuteInSampleSize方法:
public static int calcuteInSampleSize(BitmapFactory.Options options,
int reqWidth, int reqHeight) {
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if(height > reqHeight || width > reqWidth) {
final int heightRadio = Math.round((float) height / (float) reqHeight);
final int widthRadio = Math.round((float) width / (float) reqWidth);
inSampleSize = heightRadio > widthRadio ? widthRadio : heightRadio;
}
return inSampleSize;
}
返回的inSampleSize可以理解成为一个压缩倍数,比如inSampleSize=2,图片就会以原尺寸的1/2加载入内存。
但是使用的时候会遇到问题,第二次调用decodeStream()时,返回也为 null,log中会出现:
SkImageDecoder::Factory returned null
根据网上资料:链接
在BitmapFactory.decodeStream中
// we need mark/reset to work properly
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,所以建议使用BitmapFactory的其他decode方法,如果是网络读过来的流,最好在本地存成文件缓存,然后通过decodeFileDescriptor方法就没这种问题了。
我还不想去修改业务逻辑,就没使用这种方法。
使用的是另一种解决方法:
HttpClient httpClient = new DefaultHttpClient();
httpClient.getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);
HttpGet httpGet = new HttpGet(imageURI); //注意这里用的是URI,可以用
//imageURI = new URI(imageURL.getProtocol(), imageURL.getHost(), imageURL.getPath(), imageURL.getQuery(), null);将转换一下URL
HttpResponse response = (HttpResponse).httpClient.execute(httpGet);
HttpEntity entity = response.getEntity();
byte [] byteIn = EntityUtils.toByteArray(entity);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true; //设置为true,返回的bitmap为null
BitmapFactory.decodeByteArray(byteIn, 0, byteIn.length, options);
int reqHeight = Global.g_screen_height / 3;
int reqWidth = reqHeight / 5 * 3;
options.inJustDecodeBounds=false;
bitmap = BitmapFactory.decodeByteArray(byteIn, 0, byteIn.length, options);
其实就是使用了decodeByteArray的方法。
同样的,以上解决OOM的方法也可用于decodeFile()加载本地文件使用。