【Android Developers Training】 56. 更效率地

系统 1674 0

注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好。

原文链接: http://developer.android.com/training/displaying-bitmaps/load-bitmap.html


图像的的形状和尺寸千变万化。在很多情况下它们比一般的应用UI所需要的尺寸更大一些。例如,在系统图库这个应用中,显示的照片是用你的Android设备拍摄的照片,它们比起屏幕的尺寸来说要大多了。

假设你现在只有有限的内存,那么在理想情况下你希望在内存中加载分辨率更小的图片。这个低分辨率版本的图片需要和将它显示出来的UI组件的尺寸相匹配。一个过高分辨率的图片并不会带来什么改善视觉体验,反而会消耗大量的内存空间,并且因为在运行时需要调整图片的尺度而导致应用性能表现欠佳。

这节课将带你学习通过加载一个大位图的减采样版本的图片到内存中,以此来防止应用的内存空间耗尽。


一). 读取位图的尺寸和类型

BitmapFactory 类提供了一些解码方法( decodeByteArray() decodeFile() decodeResource() 等)来为不同的源创建位图。应该基于你的图像数据源选择最合适的解码方法。这些方法尝试为构建好的图像分配空间,因此它很容易导致 OutOfMemory 异常。每种解码方法都有额外的参数选项可以让你通过 BitmapFactory.Options 类指定解码选项。在解码时,将 inJustDecodeBounds 属性设置为 true 可以防止内存溢出,如果不设置 outWidth outHeight outMimeType 的话,那么就会对该图像对象返回 null 。这个技术逼迫你在构造(和分配内存时)之前先读取图像的尺寸和图像数据的类型。

      BitmapFactory.Options options = 
      
        new
      
      
         BitmapFactory.Options();

options.inJustDecodeBounds 
      
      = 
      
        true
      
      
        ;

BitmapFactory.decodeResource(getResources(), R.id.myimage, options);


      
      
        int
      
       imageHeight =
      
         options.outHeight;


      
      
        int
      
       imageWidth =
      
         options.outWidth;

String imageType 
      
      = options.outMimeType;
    

为了避免 OutOfMemory 异常,在解码图像之前检查它的尺寸,除非你完全相信提供给你图像的源会给你尺寸正好合适的图像数据,它能够符合有限的存储空间。


二). 加载一个缩小版本的图像到存储当中

现在这个图像的尺寸已经知道了,它们可以被用来这个完整的图片能否被加载到内存中,或者一个减采样的版本是否要被加载。下面是一些要考虑的因素:

  • 估计一下如果加载完整图片的话内存的使用情况。
  • 结合应用中其他内存的需求,决定这幅图片能使用多少大的内存空间。
  • 这个图片要被加载到的 ImageView 或UI组建的尺寸。
  • 当前设备的屏幕尺寸和分辨率。

例如,如果要把一幅 1024x768的 图片加载到一个大小为128x96大小的 ImageView ,显然这么做事不值得的。

为了告诉解码器对图像进行减采样,加载一个更小的版本到内存中,在你的 BitmapFactory.Options 对象中将 inSampleSize 设置为 true 。例如,一个分辨率为 2048x1536的 图像加上 inSampleSize 设置为4的选项后,会产生一幅大小为 512x384的图。将它加载进系统需要使用0.75MB而不是全尺寸图像所需要的12MB(假定位图配置为 ARGB_8888 )。下面的方法用来计算一个图像的减采样版本(如果图像过大的话):

      
        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 keeps both

        
      
      
        //
      
      
         height and width larger than the requested height and width.
      
      
        while
      
       ((halfHeight / inSampleSize) >
      
         reqHeight

                
      
      && (halfWidth / inSampleSize) >
      
         reqWidth) {

            inSampleSize 
      
      *= 2
      
        ;

        }

    }



    
      
      
        return
      
      
         inSampleSize;

}
      
    

Note:

从上述代码可以看到,在计算减采样因数时,以2为级数增加, 选择以2为级数的原因是解码器在减采样时也是选择以2为级数时最接近的那个数字做减采样的,具体的阐述可以查看: inSampleSize 的文档

为了用这个方法,首先在解码时,将 inJustDecodeBounds 设置为 true ,将选项传递进去然后再使用新的 inSampleSize 值解码,并把 inJustDecodeBounds 设置为 false

      
        public
      
      
        static
      
       Bitmap decodeSampledBitmapFromResource(Resources res, 
      
        int
      
      
         resId,

        
      
      
        int
      
       reqWidth, 
      
        int
      
      
         reqHeight) {



    
      
      
        //
      
      
         First decode with inJustDecodeBounds=true to check dimensions
      
      
        final
      
       BitmapFactory.Options options = 
      
        new
      
      
         BitmapFactory.Options();

    options.inJustDecodeBounds 
      
      = 
      
        true
      
      
        ;

    BitmapFactory.decodeResource(res, resId, options);



    
      
      
        //
      
      
         Calculate inSampleSize
      
      

    options.inSampleSize =
      
         calculateInSampleSize(options, reqWidth, reqHeight);



    
      
      
        //
      
      
         Decode bitmap with inSampleSize set
      
      

    options.inJustDecodeBounds = 
      
        false
      
      
        ;

    
      
      
        return
      
      
         BitmapFactory.decodeResource(res, resId, options);

}
      
    

这个方法使得将任意大尺寸的图片加载到值显示100x100大小的 ImageView 中时,非常方便,就像下面代码所显示的那样:

      
        mImageView.setImageBitmap(

    decodeSampledBitmapFromResource(getResources(), R.id.myimage, 
      
      100, 100));
    

你也可以通过替换适当的 BitmapFactory.decode* 方法(如果需要的话),对来自其他源的照片做类似的处理过程。

【Android Developers Training】 56. 更效率地加载大图片


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论