Android 图片加载优化

Android中通过Bitmap对象来使用图片,在加载Bitmap对象的时候,可能会导致UI线程被阻塞,用户体验差或者ANR问题;Bitmap对象迅速的消耗掉大量的内存,出现OutOfMemory异常等问题。在Android应用在加载Bitmap的时候,由于以下原因,我们需要特别的小心处理:

1.移动设备的内存资源有限,我们应该尽量的提高内存的使用效率,避免耗尽内存导致程序崩溃。

2.Bitmap对象会消耗大量内存。图片在以文件的形式存储时,可能会很小,但是当其以Bitmap的形式加载进内存的时候,将会占用大量内存,其计算公式为

 图片长度像素数*图片宽度像素数*每像素需大小,即与图片的分辨率和Bitmap配置有关。

从Android 2.3开始,使用ARGB_8888作为Bitmap的默认配置,该配置下,每个像素需要使用4Byte来表示。以我手机拍摄的一张照片为例,其分辨率为 4160*2340,大小为5.89M,当其加载进内存的时候,需要4160*2340*4/1024/1024 约为37.13M(Bitmap对象还需要保存一些关于图片的数据信息),由此可见,Bitmap对象对内存的消耗是非常巨大的。

3.在Android应用中,我们可能会在UI中同时加载多张图片,如ListView,GridView,ViewPager等

因此,我们需要对Bitmap的使用保持谨慎


在Android加载图片的过程中,我们通常使用多种优化手段来提高内存的使用效率,改善用户体验,一般来说有以下方法:

1.高效加载大图

2.在非UI线程处理Bitmap对象

3.缓存Bitmap

4.管理Bitmap的内存使用


1.高效加载大图

一般情况下,我们需要加载的图片的分辨率都会超过我们需要呈现的大小很多,例如,现在手机的摄像头1300万像素已经是标配了,其拍摄的照片的分辨率远远超过了手机屏幕的分辨率。加载一个超过屏幕分辨率的照片没有什么显而易见的好处,除非我们有特别的需求,而且这将会浪费大量的内存资源,甚至导致耗尽内存。通常我们通过对图片的缩放来获取一个与UI控件大小匹配的低分辨率的照片来进行显示。这将会大大的降低对内存的消耗,而且也不会降低用户的体验。

BitmapFactory提供了一些decode方法(decodeFile(),decodeStream(),decodeResource(),decodeByteArray())来从不同的资源中创建Bitmap,这些方法在创建Bitmap的时候会尝试分配内存,因此容易导致OOM异常的发生。因此,在通过这些方法进行解码的时候,我们可以传递一个BitmapFactory.Options对象,来指定一些设置,从而避免直接进行内存分配,同时获取图片的分辨率以及类型信息。

    BitmapFactory.Options options = new BitmapFactory.Options();
    //inJustDecodeBounds属性被设置为true时,解码将不会进行内存分配,返回的Bitmap为null
	//但是解码后Options的out*属性将会得到相应的数据,调用者可以使用这些数据。
	options.inJustDecodeBounds = true;
		
	//在5.0后这些设置被废弃了,具体作用请查看文档
	//options.inPurgeable = true;
	//options.inInputShareable = true;
		
	BitmapFactory.decodeFile("/storage/sdcard0/two.jpg",options);
		
	//解码后,可以查看Options中的out*属性的信息了
	int height = options.outHeight;
	int width = options.outWidth;
	String mineType = options.outMimeType;

在获取了图片的分辨率信息之后,我们就可以判断是加载完整的图片还是加载一个低分辨率的版本,我们需要考虑:
. 加载完整图片所需要的内存
. 程序加载图片所需要的其他内存需求
. 展示这张图片的控件的大小
. 屏幕大小与屏幕密度
例如,在一个100*100的控件上面,我们完全没有必要去加载一个1024*768图片的完整版本,我们只需要加载一个低分辨率的版本即可。我们可以在解码的时候,通过设置BitmaoFactory.Options的inSampleSize的值来指定缩放的比例。例如一个分辨率4160*2340的照片,我们可以设置inSampleSize的值为4,来获得一个宽和高都为原来的1/4的低分辨率的照片,大约为1040*585。加载完整版本的图片需要37.13M,设置inSampleSize值为4后,需要的内存将为原来的1/16(Bitmap配置都使用ARGB8888的情况下)。我们可以根据具体的需要,来计算恰当的缩放比例。

    public static int calculateInSampleSize(BitmapFactory.Options options,
			int reqWidth, int reqHeight) {
		//默认缩放值为1,不缩放
		int inSampleSize = 1;
		
		//获取完整图片的分辨率大小
		int rawWidth = options.outWidth;
		int rawHeight = options.outHeight;
		
		//如果完整图片的宽高至少有一项大于目标宽高值,那么将计算缩放
		//否则将不进行缩放
		if(rawWidth > reqWidth || rawHeight > reqHeight){
			int halfwidth = rawWidth / 2;
			int halfHeight = rawHeight / 2;
			//循环计算inSampleSize值,直到完整图片宽高与该值相除后至少有一项不大于目标宽高
			while((halfHeight / inSampleSize) > reqHeight 
					&& (halfwidth / inSampleSize) > reqWidth){
				inSampleSize *= 2;
			}
		}
		return inSampleSize;
	}

注意:缩放的值一般设置为2的幂数,因为对于非 2的幂数的值,解码器还会进行进一步处理,以获取一个最接近该值的2的幂数。

inSampleSize的值最小设置为1,此时不缩放,小于1的情况下将视为1处理。


在获取到恰当的缩放比例后,我们就可以得到一个缩放后的低分辨率版本的Bitmap了

    public static Bitmap loadSampledBitmapFromFile() {

		BitmapFactory.Options options = new BitmapFactory.Options();
		// inJustDecodeBounds属性被设置为true时,解码将不会进行内存分配,返回的Bitmap为null
		// 但是解码后Options的out*属性将会得到相应的数据,调用者可以使用这些数据。
		options.inJustDecodeBounds = true;

		// 在5.0后这些设置被废弃了,具体作用请查看文档
		// options.inPurgeable = true;
		// options.inInputShareable = true;

		BitmapFactory.decodeFile("/storage/sdcard0/two.jpg", options);

		// 解码后,可以查看Options中的out*属性的信息了
		int height = options.outHeight;
		int width = options.outWidth;
		String mineType = options.outMimeType;

		// 计算恰当的缩放比例
		options.inSampleSize = calculateInSampleSize(options, 200, 200);
		// 解码时分配内存,创建Bitmap对象
		options.inJustDecodeBounds = false;
		// 获取低分辨率版本的Bitmap对象
		return BitmapFactory.decodeFile("/storage/sdcard0/two.jpg", options);
	}


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值