Bitmap精炼详解第(一)节:Bitmap解析和加载

本文详细介绍Android中Bitmap的基础知识及高效加载方法,通过调整采样率减少内存占用,避免内存溢出,提高应用性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、前期基础知识储备

Android XML绘图技能第三讲:Bitmap位图分析,由于Bitmap的用法较Shape形状和Selector状态选择器而言要复杂的多,所以在此单独列为一个系列,集中讲解Bitmap开发中常见的知识点。

(1)Bitmap定义:上官方文档(比较尴尬 文档里没有多少东西)

Bitmap

public finalclass Bitmap

extendsObject implements Parcelable

java.lang.Object

   ↳       android.graphics.Bitmap

Bitmap百科秀一波:Bitmap(位图文件),扩展名可以是.bmp或者.dib。位图是Windows标准格式图形文件,它将图像定义为由点(像素)组成,每个点可以由多种色彩表示,包括2、4、8、16、24和32位色彩。看不懂吗?想象一下你以前测红绿色盲的时候,王医生拿给你看的那张红红绿绿小动物数字交通工具的图,那就是一个位图图片,由一个个像素组成。位图文件图像效果好,但是非压缩格式的,需要占用较大存储空间,不利于在网络上传送

(2)Bitmap的作用:Bitmap在Android中指的是一张图片,可以是png,也可以是jpg等其他图片格式。说是Android系统中图像处理中最重要的类,可以实现图像的剪切,旋转,旋转操作,并可以指定格式保存图像文件。

(3)Bitmap的使用场景举例:这里举几个常用APP,来引导读者理解Bitmap的场景:

①优快云的换头像功能,用户可以选择从相册里选取,也可以选择从相机中拍一张;

②W3CSchool的内置默认头像功能,类似的还有很多比如知乎、牛客、网易公开课等等;

③爱壁纸、百度壁纸等壁纸软件中的一张张图片加载。

软件里的头像功能是一个APP的头等大事,因为这是让用户产生归属感的最直接方式,04年QQ的火热和它的头像、签名、爱好、标签功能密不可分,这是一个人和这个这个账号的纽带,是最直接的印象。所以每个应用程序都对头像功能十分看重,而APP中能够处理头像图片、比如大小、形状、边框等等依靠的就是Bitmap。

二、Bitmap开发常用项

Bitmap涉及的知识点比较多,也比较杂,这里读者根据开发中的运用情况,列出了Bitmap的常用项,开发中最多打交道的内容:(1)图片加载;(2)图片高效加载;(3)图片压缩;(4)内存优化;(5)一些其他用法

这里本讲的内容首先Bitmap的基础用法——图片的加载、高效加载、图片压缩

(1)Bitmap的加载

BitmapFactory类,Bitmap加载离不开BitmapFactory类,关于BitmapFactory官方介绍:Creates Bitmap objects from various sources, including files,streams, and byte-arrays. BitmapFactory类共提供了四类方法用来加载Bitmap:

decodeFile文件系统(SD卡)加载

a. 通过Intent打开本地图片或照片

b. 在onActivityResult中获取图片uri

c. 根据uri获取图片的路径

d. 根据路径解析bitmap:Bitmap bm =BitmapFactory.decodeFile(sd_path)

decodeResource 以R.drawable.xxx的形式从本地资源中加载

Bitmap bm =BitmapFactory.decodeResource(getResources(), R.drawable.aaa);

decodeStream 输入流加载

a.开启异步线程去获取网络图片

b.网络返回InputStream

c.解析:Bitmap bm =BitmapFactory.decodeStream(stream),这是一个耗时操作,要在子线程中执行

decodeByteArray字节数组中加载

a.开启异步线程去获取网络图片

b.网络返回InputStream

c. 把InputStream转换成byte[]

d. 解析:Bitmap bm= BitmapFactory.decodeByteArray(myByte,0,myByte.length);

小结:Bitmap的4种基础加载方式都很简单,但是实际开发中要考虑很多问题,所以不能直接使用基本的加载方式,下面我们就开始学习Bitmap的高效加载。

————————————————————我是分隔线————————————————————

(2)Bitmap的高效加载

前文讲过,Android中的Bitmap就是一张张的JPG/PNG的图片,图片有大小之分,难道加载Bitmap不用管图片的大小吗?参见笔者之前的一篇报错文章《Error:Execution failed for task':app:processDebugResources'.个人已解决》,就可以知道,这篇文章的报错的原因就是Bitmap太大了,不能加载出来,所以报错。除了上述错误,Bitmap开发中最常见的错误,就是造成内存泄漏,内存泄漏后最终导致内存溢出,即OOM错误,然后程序崩溃。这是非常严重的错误,我们在开发中要极力避免,多以学习高效加载就尤为重要了。

如何高效的加载Bitmap呢?——对一个图片进行采样缩放

其实核心思想也很简单,那就是采用BitmapFactory.Options来加载所需尺寸的图片。这里假设通过ImageView来显示图片,很多时候ImageView并没有图片的原始尺寸那么大,这个时候把整个图片加载起来后再设给ImageView,这显然是没有必要的,因为ImageView并没有办法显示原始的图片。通过BitmapFactory.Options就可以按一定的采样率缩小并且加载的图片,将缩小后的图片在ImageView显示,这样能降低内存占用从而一定程度上避免OOM,提高了Bitmap的加载时的性能。BitmapFactory提供的加载图片的四类方法都支持BitmapFactory.Options参数,通过他们就可以很方便对一个图片进行采样缩放。

————————————————————我是分隔线—————————————————————

摘自《Android开发艺术探索》:“通过BitmapFactory.Options来缩放图片,主要利用了他的inSampleSize参数,即采样率。当inSampleSize为1时,采样后的图片大小为原始图片的原始大小;当imSampleSize大于1时,比如为2,那么采样后的图片其宽/高均为原图大小的1/2,而像素为原图的1/4,其占有的内存大小也为原图的1/4.拿一张1024*1024像素的图片来说,假定采用ARGB8888格式存储,那么他占有的内存为1024*1024*4,即4MB,如果imSampleSIze为2,那么采样后的图片其内存占用只有512*512*4,即1MB。可以发现采样率imSampleSize必须是大于1的整数才会有缩小的效果,并且采样率同时作用于宽、高,这导致缩放后的图片大小以采用率的2次方递减,即缩放比例为1/(imSamolSize的2次方),比如inSamoleSize为4,那么缩放比例就是1/16。例外最新的官方文档指出,imSampleSize的取值应该总是为2的指数,比如1,2,4,8,16等等。如果外界传递给系统的inSamoleSize不为2的指数,那么系统会向下取整并选择最接近的2的指数来代替,比如2,系统会选择2来代替,但是经过验证发现这个结论并非在所有的Android版本上都成立,因此这只是一个建议。”

看完上段话之后,接下来考虑一下实际的情况,比如ImageView的大小是100*100,而图片的原始大小为200*200,那么只需要将采样率imSampleSize设为2即可。但是如果图片大小为200*300?这个时候采样率还应该选择2,这样缩放后的图片大小为100*150像素,仍然是适合ImageView的,如果采样率为3,那么缩放后的图片大小就会小于ImageView所期望的大小,这样图片会被拉伸从而导致模糊不清。所以设置最适合的采样率imSampleSize值尤为关键。

其实正如前文所讲,“高效加载很简单”,简单就简单在采样率imSampleSize值获取比较简单,主要有以下四步:

①     BitmapFactoryFactory.Optiobs的inJustDecodeBounds参数设为true并加载图片;

②     从BitmapFactory.Options中取出图片的原始宽高信息,它们对应于outWidth和outHeight参数;

③     根据采样率的规则并结合目标View的所需大小计算出采样率inSampleSize(关键步骤)

④     将BirmipFactory.Options的inJustDecodeBounds参数设为false,然后重新加载图片。

经过上面4个步骤,加载出的图片就是最终缩放后的图片,注意:既完成了缩放也完成了加载。这里说一下inJustDecodeBounds参数,当此参数设为true时,ButmaoFactory只会解析图片的原始宽/高信息,并不会真正的加载图片,所以这个操作是轻量级的,返回值也不再是一个Bitmap对象,而是null。虽然Bitmap是null了,但是BitmapFactory.Options的outWidth、outHeight和outMimeType属性都会被赋值。这个技巧让我们可以在加载图片之前就获取到图片的长宽值和MIME类型,从而根据情况对图片进行压缩。

———————————————————我是分隔线——————————————————————

这里以decodeResource为例,进行重写,得到适配View大小的采样率:

public static Bitmap decodeSampleBitmapFronResource(Resources res,int resId,  
            int reqWidth,int reqHeight){  
        final BitmapFactory.Options options = new BitmapFactory.Options();  
        options.inJustDecodeBounds = true;  
        BitmapFactory.decodeResource(res, resId, options);  
          
        //Calculate inSampleSize  
        options.inSampleSize = calculateInSampSize(options,reqWidth,reqHeight);  
          
        //Decode bitmap with inSampleSize set  
        options.inJustFecodeBounds = false;  
        return BitmapFactory.decodeResource(res, resId, options);  
    }  
      
    public static int calculateInSampleSize(  
            BitmapFactory.Options options,int reqWidth,int reqHeight){  
        //Raw height and width of image  
        final int height = options.outHeight;  
        final int width = options.outWidth;  
        int inSampleSize = 1;  
          
        if(height > reqHeight || width > reqWidth){  
            final int halfHeight = height/2;  
            final int halfWidth = width/2;  
            //Calculate the largest inSampleSize value that is a power of 2 and  
            //keep both  
            //height nd width larger than the requested height and width  
            while((halfHeight/inSampleSize)>=reqHeight&&  
                    (halfWidth/inSampleSize)>=reqWidth){  
                inSampleSize *=2;  
                  
            }  
        }  
        return inSampleSize;  
    }

重写之后ImageView进行调用,比如ImageView所期望的图片大小为200*200像素,下面的代码非常简单地将任意一张图片压缩成200*200的缩略图,并在ImageView上展示。

imageView.setImageBitmap(  
            decodeSampledBitmapFromResource(getResources(),R.id.myimage,200,200));  

小结:上面高效加载和ImageView调用的代码可以作为模板代码,除了BitmapFactory的decodeResource方法,其他三个decode系列的方法也是支持采样加载的,并且处理方式也是类似的(但是decodeStream方法比较特殊)

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值