Android如何加载大图,防止OOM

Android加载大图,防止OOM

本文是根据Android开发文档写的,其中多次提到了堆内存,不太了解的同学可以先预习下JVM的内存模型,再来食用本文。

什么是OOM

OutOfMemory(内存泄漏),当Java虚拟机由于内存不足而无法分配对象,并且垃圾回收器无法再提供更多内存时,抛出异常OutOfMemoryError。

为什么我们的移动设备加载大图需要处理

在网络上有着许许多多的图片,有高清的,有高糊的,在pc端我们可以为所欲为,但在移动端,不可能让我们这样啊,手机的内存没有那么的大,我们的图片都是加载到内存堆中,一个app分配的内存堆是有限的,我们还要存储其他对象使用,还有一个重要因素就是虽然wifi已经普及,但还有大多数上了年纪的人对流量的概念不清楚,用你的app几分钟不仅oom了或者ANR了还欠费了(UI线程加载位图可能会降低程序性能,导致前台响应速度变慢而ANR),所以我们需要“改变图片的大小”。

处理的逻辑

显示的时候符合实际的显示规格,而不是整个图片的加载,当用户自己想看完整分辨率的图片的时候再将完整的图片载入内存显示。

处理方案

最原始的处理方案就是官网的方案,Android对位图的处理提供了整整一组的方法。这我想也应该是其他加载图片框架的基础原理。

CreateScaledBitmap

CreateScaledBitmap是Bitmap中的一个api,能更具自定义创建一个新的Bitmap,如果与原来的宽高相同则不会创建新的Bitmap。 缺点:他必须先创建一个位图。也就是说需要这个图片先被加载,解码。导致性能不高。 所以一般不采用该模式加载大图,除非你的需求是显示同一张图片不同大小。

BitmapFactory bitmapFactory = new BitmapFactory();
Bitmap bitmap = null;
try {
    bitmap = BitmapFactory.decodeStream(getAssets().open(("scg.jpeg")));
} catch (IOException e) {
    e.printStackTrace();
}
if(null != bitmap){
    //必须传入一个不为null的bitmap,宽度,高度,是否使用双线性滤波优化图片
    Bitmap changeBt = Bitmap.createScaledBitmap(bitmap, 480,240,true); 
    imgv.setImageBitmap(bitmap);
    cImgv.setImageBitmap(changeBt);
}

效果图:

 

 

 

inSampleSize

BitmapFactory.Options#inSampleSize

 

 

这是BitmapFactory.Options中的一个成员变量: 使用解码器对原图进行二次取样,接收的是一个int值,因为我们的位图就是我们的像素图,是由一个一个的小像素块组成的(你把一个图使劲放大就能看到它是由一堆小方块拼接而成的了)。小于等于1的值返回的结果都与原图一样,inSampleSize == 4 返回的图像为原始宽度/高度的1/4,像素数目的1/16。
它的原理是:行列方向每隔n格取一个像素,最后合并为一张图片

BitmapFactory.Options三件套(inScaled+inDensity+inTargetDensity)

inScaled

inScaled设置为true时(设置此标志时),如果inDensityinTargetDensity不为0,Bitmap就会在加载的时候直接进行缩放以匹配inTargetDensity,而不是绘制的时候进行缩放。(加载到堆内存时已经缩放了大小了)(.9图会忽略此标志)

inDensity

加载图片的原始宽度,如果此密度与inTargetDensity不匹配,则在返回Bitmap前会将它缩放至目标密度。

inTargetDensity

目标图片的显示宽度,它与inScaled与inDensity结合使用,确定如何在返回Bitmap前对其进行缩放。

原理

它的会进行相应的计算,进行颜色的混合,从而生成新的图片。也就是说图片越大它处理的性能也就越差。但它显示的效果要比inSampleSize的效果好,色彩还原度要高许多。

扩展(如何知道图片的原始大小?)

我们要先知道图片的原始大小,然后对其进行目标密度的缩放,但如何知道图片的原始大小呢?先加载到堆内存?然后再生成新的Bitmap?这样的话和CreateScaledBitmap不是几乎一样了吗?还是会有导致OOM的风险。 我们就要介绍inJustDecodeBounds属性,它属于BitmapFactory.Options,将其设置为true,它会进行一次图片的解析,但不会生成Bitmap对象,它返回的是bull,但他会对out...属性赋值,允许调用者查询Bitmap而不用为其分配内存。后续我们就可以使用BitmapFactory.Options的实例中的outWidth,outHeight获取原始图片的宽高,就可以进行像素的压缩了。

代码

BitmapFactory.Options mBitmapOptions = new BitmapFactory.Options();
mBitmapOptions.inJustDecodeBounds = true;

BitmapFactory.decodeResource(getResources(), R.drawable.scg, mBitmapOptions);
int srcWidth = mBitmapOptions.outWidth;
int srcHeight = mBitmapOptions.outHeight;

BitmapFactory.Options cBitmapOptions = new BitmapFactory.Options();
cBitmapOptions.inScaled = true;
cBitmapOptions.inDensity = srcWidth;
cBitmapOptions.inTargetDensity = srcWidth * 2;

Bitmap cbitmap = BitmapFactory.decodeResource(getResources(), R.drawable.scg, cBitmapOptions);

cImgv.setImageBitmap(cbitmap);
Toast.makeText(this,""+mBitmapOptions.outHeight,Toast.LENGTH_SHORT).show(); //结果:265 ,我本地的图片为265x265 
复制代码

效果图:

 

 

综合

结合上面两种方案,我们可以配套使用 inSampleSize + 三件套 。 先将图片进行快速初略压缩(inSampleSize),再进行细微的精细压缩(三件套),只测量原图的相应属性使用inJustDecodeBounds,最后就可以按需要显示“大图”了。想要原图就使用下载或者只加载对应的原位图到堆内存中就好。

总结

以上就是Android官方文档视频对位图处理的方法。我想也应该是其他框架的压缩原理。官网视频还介绍了Glide与Picass中有异步解码和缓存。(这里我想说的是这两个框架的使用方法几乎一模一样,但好像在with中的绑定是不同的,他们的绑定生命周期不同,你使用Glide的with如果绑定的是this,可能按了home再回到Activity应用会crash掉,如果有兴趣大家可以去了解一下这两个框架)

好了,文章到这里就结束了如果你觉得文章还算有用的话,不妨把它们推荐给你的朋友。

漫漫开发之路,我们只是其中的一小部分……只有不断的学习、进阶,才是我们的出路!才跟得上时代的进步!

今年年初我花一个月的时间收录整理了一套知识体系,如果有想法深入的系统化的去学习的,可以私信我【学习】,我会把我收录整理的资料都送给大家,帮助大家更快的进阶。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 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、付费专栏及课程。

余额充值