首页 > 代码库 > android客户端加载网络大图片如何避免内存溢出
android客户端加载网络大图片如何避免内存溢出
在Android开发中加载sdcard上的大图片到内存时容易导致OOM异常,常见的解决办法是基于BitmapFactory.Options类提供的方法定义指定的解码方式,设置inJustDecodeBounds属性为true,避免分配内存,返回一个null的Bitmap对象(包含
),然后读取图片的尺寸和类型。再根据屏幕的高和宽对图片进行缩放,最后将缩放的图片加载到内存,主要代码如下:outWidth
,outHeight
andoutMimeType
1 Options opts = new Options(); 2 opts.inJustDecodeBounds = true; //设置为true, 加载器不会返回图片, 而是设置Options对象中以out开头的字段.即仅仅解码边缘区域 3 BitmapFactory.decodeFile(filePath, opts); 4 5 // 得到图片的宽和高 6 int imageWidth = opts.outWidth; 7 int imageHeight = opts.outHeight; 8 9 // 获取屏幕的宽和高10 Display display = this.getWindowManager().getDefaultDisplay(); // 获取默认窗体显示的对象11 int screenWidth = display.getWidth();12 int screenHeight = display.getHeight();13 14 // 计算缩放比例15 int widthScale = imageWidth / screenWidth;16 int heightScale = imageHeight / screenHeight;17 18 int scale = widthScale > heightScale ? widthScale:heightScale;20 21 // 指定加载可以加载出图片.22 opts.inJustDecodeBounds = false;23 // 使用计算出来的比例进行缩放24 opts.inSampleSize = scale;25 Bitmap bm = BitmapFactory.decodeFile(path, opts);
这段代码主要是针对用户存储在sdcard上的大图片,如果现在要从网络上获取一张大图片并在Android机上显示出来,应该如何避免OOM异常呢?
首先加载网络大图片如果是通过BitmapFactory.decodeStream(inputStream)方法的话会出现内存溢出,所以我们同样可以考虑使用Options 类对图片进行缩放处理,然后用BitmapFactory.decodeStream(is, outPadding, opts)方法加载网络图片流对象,但是options的缩放比例该如何确定,如果直接写options.inSampleSize = 固定值;会存在两个问题:一是如果固定值太小任然有内存溢出的可能,二是如果固定值太大会导致加载出来的图片模糊不清,所以现在同样考虑根据图片的高和宽与屏幕的高和宽进行缩放?但是图片都还没加载出来怎样得到图片高和宽呢?你可能会想到通过把options.inJustDecodeBounds 设置为true; 然后调用BitmapFactory.decodeStream(is, outPadding, options),再通过options.outWidth;和options.outHeight;得到图片的高和宽,想法很好,但是当你计算出缩放比再通过options.inJustDecodeBounds = false;options.inSampleSize = scale;Bitmap bm = BitmapFactory.decodeStream(is, outPadding, options)方法获取缩放后的图片时,你会看到手机界面上啥到没有,再看日志发现输出一行SkImageDecoder::Factory returned null信息,返回的竟然是空对象,郁闷吧,这是为什么呢?原来你在代码中两次使用了BitmapFactory.decodeStream(is, outPadding, options)方法,进一步说是因为你两次使用了同一个InputStream流对象,在第二次使用时流的起始位置已经移动到末尾了,所以返回的是空,那该怎么办呢?我目前使用的方法是分别打开两个流对象,一个用于设置options的缩放比例,一个用于BitmapFactory.decodeStream(is, outPadding, options)方法调用,经过测试我发现如果先设置options后再传递给BitmapFactory.decodeStream时会非常卡,图片老半天加载不出来,所以我换了一种方式:先通过HttpClient得到图片的流对象,获取它的宽高并根据屏幕的宽高计算出缩放比例;然后再通过HttpClient得到图片的流对象,根据计算出的缩放比例进行缩放,主要代码如下:
/** * 计算图片的缩放比例 * @return */ public int getScare() { try { HttpClient client = new DefaultHttpClient(); HttpGet httpGet = new HttpGet(imageUrl); HttpResponse response = client.execute(httpGet); int code = response.getStatusLine().getStatusCode(); if (200 == code) { InputStream is = response.getEntity().getContent(); Options opts = new Options(); opts.inJustDecodeBounds = true; BitmapFactory.decodeStream(is, null, opts); int imageWidth = opts.outWidth; int imageHeight = opts.outHeight; Display display = ImageActivity.this.getWindowManager() .getDefaultDisplay(); int screenWidth = display.getWidth(); int screenHeight = display.getHeight(); int widthscale = imageWidth / screenWidth; int heightscale = imageHeight / screenHeight; int scale = widthscale > heightscale ? widthscale : heightscale; return scale; } } catch (Exception e) { e.printStackTrace(); } return 1;//网络连接失败时默认返回1 }
/** * 获取网络图片 */ protected void getNetImage() { try { HttpClient client = new DefaultHttpClient(); HttpGet httpGet = new HttpGet(imageUrl); HttpResponse response = client.execute(httpGet); int code = response.getStatusLine().getStatusCode(); if (200 == code) { InputStream is = response.getEntity().getContent(); Options opts = new Options(); //根据计算出的比例进行缩放 int scale = getScare(); opts.inSampleSize = scale; Bitmap bm = BitmapFactory.decodeStream(is, null, opts); //将bm发生给主线程用于显示图片,更新UI Message msg = Message.obtain(); msg.obj = bm; handler.sendMessage(msg); } } catch (Exception e) { e.printStackTrace(); } }
//显示图片 Handler handler = new Handler() { public void handleMessage(Message msg) { Bitmap bm = (Bitmap) msg.obj; iv.setImageBitmap(bm); }; };
android客户端加载网络大图片如何避免内存溢出