Android之Bitmap

加载Bitmap

BitmapFactory类提供了四类方法用来加载Bitmap:

1、decodeFile(...) 通过图片路径加载,同时可以选择是否设置options,不设置则采用默认options。
例子:
Bitmap bm = BitmapFactory.decodeFile(sd_path)采用默认options
Bitmap bm = BitmapFactory.decodeFile(sd_path,options)

2、decodeResource(...)通过传入Resource对象和R.drawable.xxx形式加载。
Bitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.aaa);默认options

3、decodeStream(...)通过输入流加载
Bitmap bm = BitmapFactory.decodeStream(stream),这是一个耗时操作,要在子线程中执行

4、decodeByteArray(...)从字节数组中加载。通过讲输入流inputstream转换成byte[]字节数组加载。
Bitmap bm = BitmapFactory.decodeByteArray(myByte,0,myByte.length);

** 注:**decodeFile和decodeResource间接调用decodeStream方法。

高效加载bitmap

如果图片过大,直接通过BitmapFactory加载,容易出现内存溢出。这样就需要采取一定策略来加载所需的图片。主要就是通过BitmapFactory内部的一个内部类Options来实现。

尺寸压缩 是压缩图片的像素,一张图片所占内存的大小 图片类型*宽*高,通过改变三个值减小图片所占的内存,防止OOM,当然这种方式可能会使图片失真 。这是必然的取舍。
设置Options,主要是设置图片色彩模式采样率来实现。

Android图片色彩模式分类:

Bitmap.Config.ALPHA_8*:每个像素占用1byte内存。颜色信息只由透明度组成,占8位。
Bitmap.Config.ARGB_4444每个像素占用2byte内存。颜色信息由透明度与R(Red),G(Green),B(Blue)四部分组成,每个部分都占4位,总共占16位。
Bitmap.Config.ARGB_8888每个像素占用4byte内存。颜色信息由透明度与R(Red),G(Green),B(Blue)四部分组成,每个部分都占8位,总共占32位。是Bitmap默认的颜色配置信息,也是最占空间的一种配置。
Bitmap.Config.RGB_565每个像素占用2byte内存。颜色信息由R(Red),G(Green),B(Blue)三部分组成,R占5位,G占6位,B占5位,总共占16位。

Android默认的色彩模式为ARGB_8888,这个色彩模式色彩最细腻,显示质量最高。但同样的,占用的内存也最大。

BitmapFactory.OptionsinPreferredConfig参数可以 指定decode到内存中,手机中所采用的编码,可选值定义在Bitmap.Config中。缺省值是ARGB_8888。

采样率

inSampleSize的值必须大于1时才会有效果,且采样率同时作用于宽和高;当inSampleSize=1时,采样后的图片为图片的原始大小。当inSampleSize=2时,采样后的图片的宽高均为原始图片宽高的1/2,这时像素为原始图片的1/(2x2),占用内存也为原始图片的1/(2x2);inSampleSize的取值应该总为2的整数倍,否则会向下取整,取一个最接近2的整数倍,比如inSampleSize=3时,系统会取inSampleSize=2。
假设一张1024x1024,模式为ARGB_8888的图片,inSampleSize=2,原始占用内存大小是4MB,采样后的图片占用内存大小就是(1024/2) x (1024/2 )x4 = 1MB。

public void decodeResource(View view) {
    Bitmap bm = decodeBitmapFromResource();
    imageview.setImageBitmap(bm);
}

private Bitmap decodeBitmapFromResource(){
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(getResources(), R.drawable.bbbb, options);
    options.inSampleSize = calculateSampleSize(options,300,300);
    options.inJustDecodeBounds =false;
    return  BitmapFactory.decodeResource(getResources(),R.drawable.bbbb,options);
}

// 计算合适的采样率(当然这里还可以自己定义计算规则),reqWidth为期望的图片大小,单位是px
private int calculateSampleSize(BitmapFactory.Options options,int reqWidth,int reqHeight){
    Log.i("========","calculateSampleSize reqWidth:"+reqWidth+",reqHeight:"+reqHeight);
    int width = options.outWidth;
    int height =options.outHeight;
    Log.i("========","calculateSampleSize width:"+width+",height:"+height);
    int inSampleSize = 1;
    int halfWidth = width/2;
    int halfHeight = height/2;
    while((halfWidth/inSampleSize)>=reqWidth&& (halfHeight/inSampleSize)>=reqHeight){
        inSampleSize*=2;
        Log.i("========","calculateSampleSize inSampleSize:"+inSampleSize);
    }
    return inSampleSize;
}

优点: 效率较高,解析速度快

缺点:采样率inSampleSize的取值只能是2的次方数(例如:inSampleSize=15,实际取值为8;inSampleSize=17,实际取值为16;实际取值会往2的次方结算),因此该方法不能精确的指定图片的大小

Bitmap 注意事项

1、不用的bitmap即使释放

if (!bmp.isRecycled()) { 
    bmp.recycle(); //回收图片所占的内存 
    bitmap = null; 
    system.gc(); //提醒系统及时回收
} 

2、捕获OutOfMemoryError

bitmap在实例化的过程中是很耗内存的。很容易出现OutOfMemery内存溢出的情况。而且一出现程序就会crash。所以,需要对bitmap的实例化的时做OutOfMemoryError捕获,需要注意的是OutOfMemoryError并不是异常而是错误。一般情况下java中异常是可以捕获的。而错误是不可以的,因为Error的出现一般情况下程序就会终止。OutOfMemoryError比较特殊。

Bitmap bitmap = null; 
try { 
    // 实例化Bitmap 
    bitmap = BitmapFactory.decodeFile(path); 
} catch (OutOfMemoryError e) { 
>      // 如果实例化失败 返回默认的Bitmap对象
     return defaultBitmapMap; 
} 

3、缓存通用的bitmap对象

在加载用户头像的时候,如果用户没有上传的头像,一般会加载一个默认头像。而用户的默认头像又是一样的,所以,对于相同头像的bitmap应该做缓存处理。如果不进行缓存,尽管看到的是同一张图片文件,但是使用BitmapFactory类的方法来实例化出来的Bitmap,是不同的Bitmap对象。缓存可以避免新建多个Bitmap对象,避免内存的浪费。

4、图片质量压缩
上述用inSampleSize压缩是尺寸压缩,Android中还有一种压缩方式叫质量压缩。质量压缩是在保持像素的前提下改变图片的位深及透明度等,来达到压缩图片的目的,经过它压缩的图片文件大小(kb)会有改变,但是导入成bitmap后占得内存是不变的,宽高也不会改变。因为要保持像素不变,所以它就无法无限压缩,到达一个值之后就不会继续变小了。显然这个方法并不适用与缩略图,其实也不适用于想通过压缩图片减少内存的适用,仅仅适用于想在保证图片质量的同时减少文件大小的情况而已

private void compressImage(Bitmap image, int reqSize) { 
    ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
    // 质量压缩方法,这里100表示不压缩, 
    image.compress(Bitmap.CompressFormat.JPEG, 100, baos);
    int options = 100; 
> 
    // 循环判断压缩后的图片是否大于reqSize,大于则继续压缩 
    while (baos.toByteArray().length / 1024 > reqSize) { 
        baos.reset();//清空baos    
        // 这里压缩options,把压缩后的数据放到baos中 
        image.compress(Bitmap.CompressFormat.JPEG, options, baos);
        options -= 10; 
} 
// 把压缩后的baos放到ByteArrayInputStream中 
ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray()); 
//decode图片 
Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null); }

Android加载大量图片内存溢出解决方案

①、在加载图片的时候,尽量不要直接使用setImageBitmapsetImageResourceBitmapFactory.decodeResource来设置一张大图,因为这些函数在完成decode后,最终都是通过java层的createBitmap来完成的,需要消耗更多内存,可以通过BitmapFactory.decodeStream方法,创建出一个bitmap,再将其设为ImageView的 source

③、运用Java软引用,进行图片缓存,将需要经常加载的图片放进缓存里,避免反复加载

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值