Android加载大图策略,防止OOM

前言


Android中图片以位图(Bitmap)的形式存在,位图常见的格式有.png.jgp.bmp.gif。在加载图片的过程中常见的就是OOM(Out of Memory)内存溢出。

内存溢出是系统会给APP分配内存也就是Heap Size值。当APP占用的内存加上我们申请的内存资源超过了Dalvik虚拟机的最大内存时就会抛出的Out Of Memory异常。

为什么在加载大图片时会出现内存溢出?首先我们要了解Bitmap位图占用内存大小的计算方式。


一、颜色格式与内存大小计算


常用的颜色格式有ALPHA_8ARGB_4444ARGB_8888RGB_565

其中,A代表透明度;R代表红色;G代表绿色;B代表蓝色。

ALPHA_8 表示8位Alpha位图,即A=8,一个像素点占用1个字节,它没有颜色,只有透明度

ARGB_4444 表示16位ARGB位图,即A=4,R=4,G=4,B=4,一个像素点占4+4+4+4=16位,2个字节

ARGB_8888 表示32位ARGB位图,即A=8,R=8,G=8,B=8,一个像素点占8+8+8+8=32位,4个字节

RGB_565 表示16位RGB位图,即R=5,G=6,B=5,它没有透明度,一个像素点占5+6+5=16位,2个字节

占用内存 = 图片宽度(像素) * 图片高度(像素) * 单个像素所占内存空间(单位:字节)
例如像素为3840 * 2160 ,颜色格式为ARGB8888,每个像素占8位,总共32位,4个字节。

占用内存大小 = 3840 * 2160 * 4 / 1024 / 1024 = 31.64M

计算得出,一张4K图片在内存中约占32M内存,加载更高分辨率的图片是很容易造成OOM,所以在加载大图前对图片进行压缩,下面将介绍Android中常用的4种压缩方式。


二、图片压缩

下面将介绍4种压缩方式,分别为质量压缩采样率压缩缩放压缩颜色格式压缩


1、质量压缩

质量压缩适合于分辨率不大,但是图片大小较大的情况,在Bitmap中提供了compress方法。

    /**
     * @param[quality]:0-100 ,数值越小代表压缩率越高
     */
    fun compressByQuality(quality:Int,ctx: Context){
        val bitmap = BitmapFactory.decodeResource(ctx.resources,R.mipmap.wallpaper_00)
        val bos = ByteArrayOutputStream()
        bitmap.compress(Bitmap.CompressFormat.JPEG,quality,bos)
        val compressBitmap = BitmapFactory.decodeByteArray(bos.toByteArray(),0,bos.toByteArray().size)
    }

质量压缩有以下缺点:

  • 改变图片的位深和透明度,不改变图片的分辨率,所以不改变在内存中的大小。
  • 质量压缩对png格式图片压缩无效,png图片是无损压缩格式。

2、采样率压缩

采样率压缩是相对于质量压缩的优点是可以改变图片在内存中的大小,在进行采样率压缩前,我们先了解下对应的属性。

    val dm = ctx.resources.displayMetrics
    val option = BitmapFactory.Options().apply {
         inJustDecodeBounds = false
         //可以复用之前用过的bitmap
         inBitmap = null

         //是该bitmap缓存是否可变,如果设置为true,将可被inBitmap复用
         inMutable = true

         //表示这个bitmap的像素密度,当inDensity为0时,系统默认赋值为屏幕当前像素密度
         inDensity = dm.density.toInt()

         //表示要被画出来时的目标像素密度,当inTargetDensity为0时,系统默认赋值为屏幕当前像素密度
         inTargetDensity = inDensity

         //表示实际设备的像素密度
         inScreenDensity = 0

         //这个参数可以改变bitmap分辨率大小,inSampleSize >= 1。
         //当inSampleSize < 1时,inSampleSize就默认是1。
         //假如:图片的宽和高分别是width、height,那么图片解码生成的bitmap的宽度是:width / inSampleSize,高度是:height / inSampleSize
         //inSampleSize影响bitmap的分辨率,从而影响bitmap占用内存的大小。
         inSampleSize = 2

         //表示图片是否可以被缩放
         inScaled = true

         //A R G B 四个颜色通道 每个通道占8位
         inPreferredConfig = Bitmap.Config.ARGB_8888
     }

采样率压缩主要用到BitmapFactory.Options中的属性inSampleSize,这个参数设置为大于或等于1。等于1默认为不压缩。

下面介绍一个比较重要的参数inJustDecodeBounds。当设置inJustDecodeBounds = true时,表示仅仅解码图片的图片的大小,不将图片加载进内存中。

 
  val option = BitmapFactory.Options().apply {
          inJustDecodeBounds = true
      }
  val bitmap = BitmapFactory.decodeResource(ctx.resources, R.mipmap.wallpaper_00,option)
  val imgW = option.outWidth
  val imgH = option.outHeight
  Log.i(TAG, "inJustDecodeBounds = ${option.inJustDecodeBounds} ,bitmap = $bitmap ,imgW = $imgW ,imgH = $imgH")
  

在这里插入图片描述

可以看到返回的Bitmap对象为空,一般当我们要获取加载图片的宽和高来选择最优的采样率压缩比时,可以设置此属性来获取图片的相关信息。

当设置inJustDecodeBounds = false时,来设置inSampleSize = 4后看下压缩效果。

  
    val option = BitmapFactory.Options()

    option.inJustDecodeBounds = true
    val bitmap = BitmapFactory.decodeResource(ctx.resources, R.mipmap.wallpaper_00,option)
    val preImgW = option.outWidth
    val preImgH = option.outHeight
    Log.i(TAG, "inJustDecodeBounds = ${option.inJustDecodeBounds} ,bitmap = $bitmap ,preImgW = $preImgW ,preImgH = $preImgH")

    option.inJustDecodeBounds = false
    option.inSampleSize = 4
    val afterBitmap = BitmapFactory.decodeResource(ctx.resources, R.mipmap.wallpaper_00,option)
    val afterW = option.outWidth
    val afterH = option.outHeight
    Log.i(TAG, "inJustDecodeBounds = ${option.inJustDecodeBounds} ,bitmap = $afterBitmap ,inSampleSize = ${option.inSampleSize} ,afterW = $afterW ,afterH = $afterH")
    

在这里插入图片描述

在设置inSampleSize = 4,图片的宽和高的分辨率都缩小到了原来的1/4。此时再计算图片在内存的大小为

内存大小 = 960 * 540 * 4 / 1024 / 1024 = 1.98M

与未压缩的图片在内存中所在31.64M相比,缩小了约16倍,这也达到了在压缩图片所占内存大小的目的。


3、缩放压缩

此方法是将图片缩放到指定分辨率,其实是和采样率压缩是相同效果的不同方法,通过Matrix矩阵进行缩放变换。

 	
 	val srcBitmap = BitmapFactory.decodeResource(ctx.resources,R.mipmap.wallpaper_00)

	val srcW = srcBitmap.width
	val srcH = srcBitmap.height

	val newW = 960
	val newH = 540

    val scaleW = newW / srcW.toFloat()
    val scaleH = newH / srcH.toFloat()

    val matrix = Matrix()
    matrix.setScale(scaleW,scaleH)
    
    val newBitmap = Bitmap.createBitmap(srcBitmap,0,0,srcW,srcH,matrix,true)
    

4、颜色格式压缩

上面介绍了4种不同的颜色格式,Android中默认使用的是ARGB_8888,4个字节,可以将此格式设置成RGB_565。对应的是BitmapFactory.Options中的inPreferredConfig属性。

 
   val option = BitmapFactory.Options()
        option.inPreferredConfig = Bitmap.Config.RGB_565
        
   val newBitmap = BitmapFactory.decodeResource(ctx.resources,R.mipmap.wallpaper_00,option)
   

结尾

为了防止应用OOM发生,上述介绍了Android中加载大图的几种压缩方法,可根据项目中具体的需求来使用不同的压缩方式。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android OOM(Out of Memory)是一种常见的运行时异常,指的是应用程序内存不足的错误。当应用程序试图使用超过系统分配给它的内存时,就会出现这种异常。这可能是由于应用程序在后台加载大量数据、存储过多的对象或图像,或者由于系统资源管理器分配的内存不足所致。 为了解决Android OOM问题,您可以采取以下几种策略: 1. 优化您的代码以减少内存使用量:使用正确的数据类型,避免创建不必要的对象,限制图像和资源的数量,以及优化后台加载过程等。 2. 回收不再使用的内存:当您的应用程序不再需要使用某些内存时,应该及时回收它们。这可以通过调用垃圾回收器(Garbage Collector)来完成。 3. 避免在主线程上执行耗时操作:如果您的应用程序在主线程上执行耗时操作(如大量数据处理),这可能导致系统资源管理器超载,从而引发OOM异常。应该将这些操作移至后台线程。 4. 使用内存分析工具:内存分析工具可以帮助您识别内存泄漏和无效内存引用等问题,从而避免OOM异常的发生。 5. 配置您的应用程序以适应不同的内存配置:如果您正在开发一个需要大量内存的应用程序,您应该考虑在AndroidManifest.xml文件中配置您的应用程序以适应不同的内存配置。例如,您可以设置您的应用程序需要的最低和最高内存限制。 请注意,解决Android OOM问题是一个复杂的过程,需要您仔细分析和优化您的代码。如果您遇到了OOM问题,建议寻求专业的帮助或与开发社区进行讨论。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值