图片加载的相关话题

一张大图如何加载才不会发生OOM

这是面试官经常问的问题具体有两种策略
1.图片采样率缩放。
2.按区域加载按区域加载。
在说这两种策略之前需要先讲另一个问题,一张图片占多少内存,是由哪些因素决定的?
1.图片的宽高。
2.图片加载的模式。
3.图片的缩放比例。
一般认为图片的内存计算公式是这样的:
图片占用内存的大小 = 宽单个像素占用的字节数
单个像素占用的字节数则是图片加载的模式决定的例如:
Android中默认使用ARGB_8888的模式加载图片,即一个像素点占用2byte。这里简单介绍一下各个模式:
1.ALPHA_8:像素点只有alpha通道,1个像素点占用1byte。这个模式下只有透明度,一般用来做遮罩层。
2.RGB_565:R通道占用5bit,G通道占用6bit,B通道占用5bit,总共占用2byte。这个模式没有ALPHA通道,对于色彩要求不高的情况,可以采用RGB_565,Glide默认使用这个模式。
3.ARGB_4444:ARGB四个通道分别占用4bit,总共占用2byte。色彩表现不好,已经被废弃了,在4.4之后使用ARGB_4444会默认使用ARGB_8888替代。
4.ARGB_8888:ARGB四个通道分别占用8bit,总共占用4byte。比较灵活,色彩表现度较好,是推荐使用的模式,也是默认使用的模式。
5.RGBA_F16:一个像素占用8byte,适用于广色域显示。实际开发中暂时还没见到用这个的场景。

如果是系统资源,图片所存放的文件夹的路径不同。图片的缩放比例也是不同的。
这里写图片描述

如果你把一张图片放在了mdpi,而用户的手机是xhdpi的话,你会发现你的图片实际所占用的内存会比计算值(按照上面的方法做计算)多4倍。所以如果你把上面那张图片放在了mdpi目录下面,这简直就是自残啊:占用内存120M。

这是因为安卓系统会把mdpi的图片尺寸进行扩大以保证显示的尺寸相同。而相反的如果你把图片放到了xhdpi,而你的手机是mdpi的话,你的图片会被压缩成原来的1/4。
具体的比例关系:(NX 表示表示长和宽是mdpi的N倍,总内存就是N*N倍)

图片采样率缩放。

主要用于减少图片的像素点来达到减少内存的效果。主要用到的是BitmapFactory.Options类实现。

inJustDecodeBounds:该属性设置为true时,只加载图片信息到Options中,而不将图片加载到内存中。
outWidth:图片的宽度。
outHeight:图片的高度。
inSampleSize:采样率,如果值大于1,将对图片进行二次采样,宽和高分别按相应的比例缩小。例如设置为2,则宽和高缩小为原来的2分之一,即200*200的图片缩小成100*100,图片占用的内存则为原来的4分之一。inSampleSize应该设置为2的n次方,否则向下取最近的2的n次方数。比如设置inSampleSize=10,则实际inSampleSize向下取2的3次方为8,因此缩小成8分之一。

具体代码如下:

BitmapFactory.Options options = new BitmapFactory.Options();
            //只读取图片信息
            options.inJustDecodeBounds = true;
            BitmapFactory.decodeResource(getResources(), R.drawable.ad, options);
            //读取图片宽和高
            int imgWidth = options.outWidth;
            int imgHeight = options.outHeight;
            //读取ImageView宽和高
            int ivHeight = imageView.getHeight();
            int ivWidth = imageView.getWidth();
            //分别计算宽和高的比值
            int scaleX = imgWidth / ivWidth;
            int scaleY = imgHeight / ivHeight;
            int inSampleSize = 1;
            //取较大的值作为采样率
            inSampleSize = scaleX > scaleY ? scaleX : scaleY;
            //防止采样率小于1
            if (inSampleSize < 1)
                inSampleSize = 1;
            Log.i("bitmap", "inSampleSize = " + inSampleSize);
            //根据采样率加载图片
            options.inJustDecodeBounds = false;
            options.inSampleSize = inSampleSize;
            Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ad, options);
            imageView.setImageBitmap(bitmap);

按区域加载

如果不想压缩图拍则需要按区域加载,主要用到的类为BitmapRegionDecoder类实现,用到的参数主要是Rect和BitmapFactory.Options,其中Rect指定加载图片的区域。代码如下:

BitmapFactory.Options options = new BitmapFactory.Options();
                options.inJustDecodeBounds = true;
                BitmapFactory.decodeResource(getResources(), R.drawable.ad, options);
                //老套路,计算图片的宽和高
                int imgWidth = options.outWidth;
                int imgHeight = options.outHeight;
                InputStream is = getResources().openRawResource(R.raw.ad);
                //初始化BitmapRegionDecoder
                BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(is, false);
                //设置图片加载模式为RGB_565
                options.inPreferredConfig = Bitmap.Config.RGB_565;
                //加载图片中心位置300px*300px大小的图片
                Bitmap bitmap = decoder.decodeRegion(new Rect(imgWidth/2 - 150, imgHeight/2 - 150, imgWidth/2 + 150, imgHeight/2 + 150), options);
                imageView.setImageBitmap(bitmap);

本文主要参考如下博客:
https://blog.csdn.net/forevercbb/article/details/81205681

https://www.jianshu.com/p/b8f5f7f59edd

https://mp.weixin.qq.com/s/kIu-M_Fc-EGKZiPqxb_OjQ

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值