高效加载大型位图

高效加载大型位图

注意:有几个库遵循了加载图片的最佳做法。您可以在应用中使用这些库,从而以最优化的方式加载图片。我们建议您使用 Glide(https://github.com/bumptech/glide) 库,该库会尽可能快速、顺畅地加载和显示图片。其他常用的图片加载库包括 Square 的 Picasso、Instacart 的 Coil和 Facebook 的 Fresco。这些库简化了与位图和 Android 上的其他图片类型相关的大多数复杂任务。

图片有各种形状和大小。在很多情况下,它们的大小超过了典型应用界面的要求。例如,系统“图库”应用会显示使用 Android 设备的相机拍摄的照片,这些照片的分辨率通常远高于设备的屏幕密度(屏幕分辨率)

鉴于您使用的应用内存有限,理想情况下您只希望在内存中加载较低分辨率的版本。分辨率较低的版本应与显示该版本的界面组件的大小相匹配。分辨率更高的图片不会带来任何明显的好处,但仍会占用宝贵的内存,并且会因为额外的动态缩放而产生额外的性能开销

本节课向您介绍如何通过在内存中加载较小的下采样版本来解码大型位图Bitmap,从而不超出每个应用的内存限制(Android 不同厂商,不同版本的设备,应用使用内存大小限制不一样)

读取位图尺寸和类型

BitmapFactory类提供了几种用于从各种来源创建 Bitmap解码方法(decodeByteArray()decodeFile()decodeResource()等)。根据您的图片数据源选择最合适的解码方法。这些方法尝试为构造的位图分配内存,因此很容易导致 OutOfMemory 异常(OOM = OutOfMemoryError,这个异常可以捕获吗?请思考,见文末)。每种类型的解码方法都有额外的签名,允许您通过 BitmapFactory.Options类指定解码选项。在解码时将 inJustDecodeBounds属性设置为 true 可避免内存分配,为位图对象返回 null,但设置了Bitmap的 outWidthoutHeightoutMimeType。此方法可让您在构造位图并为其分配内存之前读取图片数据的尺寸和类型。

Java

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; // 图片类型

kotlin

   val options = BitmapFactory.Options().apply {
        inJustDecodeBounds = true
    }
    BitmapFactory.decodeResource(resources, R.id.myimage, options)
    val imageHeight: Int = options.outHeight
    val imageWidth: Int = options.outWidth
    val imageType: String = options.outMimeType

为避免出现 java.lang.OutOfMemory 异常,请先检查位图的尺寸,然后再对其进行解码,除非您绝对信任该来源可为您提供大小可预测的图片数据,以轻松适应可用的内存。

将按比例缩小的版本加载到内存中

既然图片尺寸已知,便可用于确定
应将完整图片加载到内存中,还是应改为加载下采样版本。以下是需要考虑的一些因素:

  • 在内存中加载完整图片的估计内存使用量。(怎么算图片内存大小?见文末
  • 根据应用的任何其他内存要求,您愿意分配用于加载此图片的内存量。
  • 图片要载入到的目标 ImageView或界面组件的尺寸。
  • 当前设备的屏幕大小和密度。

例如,如果 1024x768 像素的图片最终会在 ImageView中显示为 128x96 像素缩略图,则不值得将其加载到内存中。
要让解码器对图片进行下采样,以将较小版本加载到内存中,请在 BitmapFactory.Options 对象中将 inSampleSize 设置为 true。例如,分辨率为 2048x1536 且以 4 作为 inSampleSize 进行解码的图片会生成大约 512x384 的位图。将此图片加载到内存中需使用 0.75MB,而不是完整图片所需的 12MB(假设位图配置为 ARGB_8888)。下面的方法用于计算样本大小值,即基于目标宽度和高度的 2 的幂:
Java

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

kotlin

 fun calculateInSampleSize(options: BitmapFactory.Options,
 reqWidth: Int, reqHeight: Int): Int {
        // Raw height and width of image
        val (height: Int, width: Int) = options.run { 
              outHeight to outWidth 
        }
        var inSampleSize = 1
        if (height > reqHeight || width > reqWidth) {
            val halfHeight: Int = height / 2
            val halfWidth: Int = 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
    }

注意:根据 inSampleSize 文档,计算 2 的幂的原因是解码器使用的最终值将向下舍入为最接近的 2 的幂。

Note: the decoder uses a final value based on powers of 2, 
any other value will be rounded down to the nearest power of 2.

要使用此方法,请先将 inJustDecodeBounds设为 true 进行解码,传递选项,然后使用新的 inSampleSize值并将 inJustDecodeBounds 设为 false 再次进行解码:
JAVA

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

KOTLIN

fun decodeSampledBitmapFromResource(
            res: Resources,
            resId: Int,
            reqWidth: Int,
            reqHeight: Int
    ): Bitmap {
        // First decode with inJustDecodeBounds=true to
      // check dimensions
        return BitmapFactory.Options().run {
        inJustDecodeBounds = true
        BitmapFactory.decodeResource(res, resId, this)
         // Calculate inSampleSize
        inSampleSize = calculateInSampleSize(this, reqWidth, reqHeight)
            // Decode bitmap with inSampleSize set
        inJustDecodeBounds = false
        BitmapFactory.decodeResource(res, resId, this)
        }
    }

采用此方法,您可以轻松地将任意大尺寸的位图加载到显示 100x100 像素缩略图的 ImageView中,如以下示例代码所示:

JAVA

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

KOTLIN

 imageView.setImageBitmap(
  decodeSampledBitmapFromResource(resources, R.id.myimage, 100, 100))

您可以按照类似的流程来解码其他来源的位图,只需根据需要替换相应的 BitmapFactory.decode*方法即可。


1、关于异常的捕获 :

try{    

}catch(Exception e){    
}
try{    
// 加载大图片 ,OutOfMemoryError
}catch(Error e){    
// 加载图片失败
}
try{         

}catch (Throwable throwable){
}

Exception 和 Error 都继承了 Throwable 
catch 是能捕获 Throwable 和 exception 和 error

面试题: OutOfMemoryError能捕获吗 ? 上面的例子是可以的!
图片加载的时候避免OOM!

2、Bitmap 加载内存的大小计算

/*   A bitmap configuration describes
      how pixels are stored. This affects the quality (color depth) as
      well as the ability to display transparent/translucent colors.
*/
Bitmap.config  // 描述像素如何被存储,这影响质量(颜色深度)
                        //以及显示透明/半透明颜色的能力。

Bitmap.Config.ALPHA_8 :只有一个alpha通道,占用1Byte。

Bitmap.Config.RGB_565:每个像素占2Byte,其中红色占5bit,绿色占6bit,蓝色占5bit。

@Deprecated 
Bitmap.Config.ARGB_4444:每个像素占2Byte,每个通道4bit,从API 13开始不建议使用。

Bitmap.Config.ARGB_8888:每个像素占4Byte,每个通道8bit。
这个格式最常用,通常是用于PNG的图片,支持透明度。

val bitmap = Bitmap.createBitmap(100,100,Bitmap.Config.ARGB_8888)

图片像素的长 * 宽 * 存储字节 :

100 * 100 * 4 = 40000 byte = 39 Kb


From:https://developer.android.com/topic/performance/graphics/load-bitmap

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,039评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,426评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,417评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,868评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,892评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,692评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,416评论 3 419
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,326评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,782评论 1 316
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,957评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,102评论 1 350
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,790评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,442评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,996评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,113评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,332评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,044评论 2 355

推荐阅读更多精彩内容