android加载图片Out Of Memory的解决

来源:互联网 发布:软件系统功能分析 编辑:程序博客网 时间:2024/05/18 16:37

<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()加载本地文件使用。

0 0