android客户端加载网络大图片如何避免内存溢出

在Android开发中加载sdcard上的大图片到内存时容易导致OOM异常,常见的解决办法是基于BitmapFactory.Options类提供的方法定义指定的解码方式,设置inJustDecodeBounds属性为true,避免分配内存,返回一个null的Bitmap对象(包含outWidth,outHeightandoutMimeType),然后读取图片的尺寸和类型。再根据屏幕的高和宽对图片进行缩放,最后将缩放的图片加载到内存,主要代码如下:

 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);
        };
    };

 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。