「性能优化系列」不使用第三方库,Bitmap的优化策略

如今市场上有很多封装好的第三方库,对Bitmap内存也是做到了很好的优化,比如Glide、Fresco,每次加载只要直接调用就好,但是除掉第三方库外,我们还是需要去了解一下Bitmap的基本优化手段。

一、Bitmap内存进程

首先我们有必要去了解一下Bitmap的基本知识点,在Android3.0之前,Bitmap的对象是放在Java堆中,而Bitmap的像素是放置在Native内存中,这个时候需要手动的去调用recycle,才能去回收Native内存;

在Android3.0到Android7.0,Bitmap对象和像素都是放置到Java堆中,这个时候即使不调用recycle,Bitmap内存也会随着对象一起被回收。虽然Bitmap内存可以很容易被回收,但是Java堆的内存有很大的限制,也很容易造成GC。

在Android8.0的时候,Bitmap内存又重新放置到了Native中。

Bitmap造成OOM很多时候也是因为对Bitmap的资源没有得到很好的利用,同时没有做到及时的释放。

二、优化策略

对于Bitmap的优化主要分为针对不同密度的设备合理的分配资源,压缩以及缓存处理三种。

2.1.drawable的合理分配

总所周知,drawable时放置本地图片资源的地方,从上图可以发现,AS将drawable分为了mdpi,hdpi,xhdpi…不同的等级,简单概括为不同等级的dpi代表着不同的设备密度,它们之间的区别暂时先不论,有必要先去了解一下AS对于drawable的匹配规则.

举个例子,当当前的设备密度为xhdpi,此时代码中ImageView需要去引用drawable中的图片,那么根据匹配规则,系统首先会在drawable-xhdpi文件夹中去搜索,如果需要的图片存在,那么直接显示;如果不存在,那么系统将会开始从更高dpi中搜索,例如drawable-xxhdpi,drawable-xxxhdpi,如果在高dpi中搜索不到需要的图片,那么就会去drawable-nodpi中搜索,有则显示,无则继续向低dpi,如drawable-hdpi,drawable-mdpi,drawable-ldpi等文件夹一级一级搜索.

当在比当前设备密度低的文件夹中搜到图片,那么在ImageView(宽高在wrap_content状态下)中显示的图片将会被放大.图片放大也就意味着所占内存也开始增多.这也就是为什么分辨率不高的图片随意放置在drawable中也会出现OOM.而在高密度文件夹中搜到图片,图片在该设备上将会被缩小,内存也就相应减少.

在理想的状态下,不同dpi的文件下应该放置相应dpi的图片资源,以对不同的设备进行适配.但在图片资源没有做dpi区分的时候,根据以上所说的匹配规则,将图片资源放置在高dpi 如drawable-xdpi,drawable-xxdpi文件夹中.是比较好的选择,在最大程度上减少OOM的几率。

2.2.尺寸优化

当装载图片的容器例如ImageView只有100*100,而图片的分辨率为800 * 800,这个时候将图片直接放置在容器上,很容易OOM,同时也是对图片和内存资源的一种浪费。当容器的宽高都很小于图片的宽高,其实就需要对图片进行尺寸上的压缩,将图片的分辨率调整为ImageView宽高的大小,一方面不会对图片的质量有影响,同时也可以很大程度上减少内存的占用。

对于尺寸压缩首先需要去了解一个知识点inSampleSize,


从上图发现Android官方对它的解释是,如果inSampleSize 设置的值大于1,则请求解码器对原始的bitmap进行子采样图像,然后返回较小的图片来减少内存的占用,例如inSampleSize == 4,则采样后的图像宽高为原图像的1/4,而像素值为原图的1/16,也就是说采样后的图像所占内存也为原图所占内存的1/16;当inSampleSize <=1时,就当作1来处理也就是和原图一样大小。另外最后一句还注明,inSampleSize的值一直为2的幂,如1,2,4,8。任何其他的值也都是四舍五入到最接近2的幂。

采样率inSampleSize其实是一个规定图片压缩倍数的一个参数,通过图片宽高的比较得到一个新的数值,inSampleSize设置到BitmapFactory中重新去解码图片。下面就是利用inSampleSize对图片进行尺寸上的优化代码。

/**
     * 对图片进行解码压缩。
     *
     * @param resourceId 所需压缩的图片资源
     * @param reqHeight  所需压缩到的高度
     * @param reqWidth   所需压缩到的宽度
     * @return Bitmap
     */
    private Bitmap decodeBitmap(int resourceId, int reqHeight, int reqWidth) {
   
        BitmapFactory.Options options = new BitmapFactory.Options();
        //inJustDecodeBounds设置为true,解码器将返回一个null的Bitmap,系统将不会为此Bitmap上像素分配内存。
        //只做查询图片宽高用。
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(getResources(), resourceId, options);
        //查询该图片的宽高。
        int height = options.outHeight;
        int width = options.outWidth;
        int inSampleSize = 1;

        //如果当前图片的高或者宽大于所需的高或宽,
        // 就进行inSampleSize的2倍增加处理,直到图片宽高符合所需要求。
        if (height > reqHeight || width > reqWidth) {
   
            int halfHeight = height / 2;
            int halfWidth = width / 2;
            while ((halfHeight / inSampleSize >= reqHeight)
                    && (halfWidth / inSampleSize) >= reqWidth) {
   
                inSampleSize *= 2;
            }
        }

        //inSampleSize获取结束后,需要将inJustDecodeBounds置为false。
        options.inJustDecodeBounds = false;
        //返回压缩后的Bitmap。
        return BitmapFactory.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值