Android bitmap实际使用总结

实际开发过程中,为了珍惜的android内存,防止oom,尽量减少bitmap,也就是图片加载进内存。但是我们有些时候比可避免的要使用到bitmap。

这里写代码片

//从硬盘获取得到bitmap:底层使用的是 decodeStream 用stream 会节省内存
Bitmap bm = BitmapFactory.decodeFile(file.getAbsolutePath());

//从网络获取的流中得到bitmap 也可以是本地的流
Bitmap bm = BitmapFactory.decodeStream(is);

//从返回(activity之间跳动:不能传递bitmap对象)的数组中得到bitmap
byte[] photobyte=extras.getByteArray(“bitmap”);
BitmapFactory.decodeByteArray(photobyte, 0, photobyte.length)

//加载resource
BitmapFactory.decodeResource()

//bitmap通过流写进硬盘:准备一个写进硬盘的流关联硬盘存储的文件:也是质量压缩 0到100
bitmap.compress(CompressFormat.JPEG, 100, os);

File file = new File(CACHE_DIR, fileName);// /sdcard/adsfas/zhbj71/jk;ljsljl
File parentFile = file.getParentFile();
if(!parentFile.exists()){// 判断上级目录是否存在,不存在就需要创建
parentFile.mkdirs();
}
// 把Bitmap对象持久化到本地
OutputStream os = new FileOutputStream(file);
bitmap.compress(CompressFormat.JPEG, 100, os);//数表示的是图片的质量

//将bitmap 转成 字节数组
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 75, baos);//数表示的是图片的质量
byte[] bitmapByte = baos.toByteArray();

//备注:bitmap加载进来 用stream 会节省内存 :但是最好还是用图片尺寸压缩最好!
Uri uri = data.getData();
Log.e(“uri”, uri.toString());
ContentResolver cr = this.getContentResolver();
try {
Bitmap bitmap = BitmapFactory.decodeStream(cr.openInputStream(uri));
ImageView imageView = (ImageView) findViewById(R.id.iv01);

            imageView.setImageBitmap(bitmap);    

最好的节省内存:bitmap尺寸压缩:注意传入的String image 路径 不能有前缀:uri
public static Bitmap decodeSampledBitmapFromResource(String image, int reqWidth, int reqHeight) {

    // 给定的BitmapFactory设置解码的参数
    final BitmapFactory.Options options = new BitmapFactory.Options();
    // 从解码器中获取原始图片的宽高,这样避免了直接申请内存空间
    options.inJustDecodeBounds = true;
    Bitmap bitmap = BitmapFactory.decodeFile(image, options);
    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth,
            reqHeight);
    // 压缩完后便可以将inJustDecodeBounds设置为false了。
    options.inJustDecodeBounds = false;
    bitmap = BitmapFactory.decodeFile(image, options);
    return bitmap;
}
/**
 * 指定图片的缩放比例
 *
 * @param options
 * @param reqWidth
 * @param reqHeight
 * @return
 */
public static int calculateInSampleSize(BitmapFactory.Options options,
                                        int reqWidth, int reqHeight) {
    // 原始图片的宽、高
    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;
// // while ((halfHeight / inSampleSize) > reqHeight
// // && (halfWidth / inSampleSize) > reqWidth) {
// // inSampleSize *= 2;
// // }
/**
* 压缩方式一
*/
// 计算压缩的比例:分为宽高比例
final int heightRatio = Math.round((float) height
/ (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
// }

    return inSampleSize;
}

Bitmap bitmap = imageView.clip();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 75, baos);
byte[] bitmapByte = baos.toByteArray();
Log.e(“jxf”,bitmapByte.toString());
Intent intent=new Intent();
intent.putExtra(“bitmap”, bitmapByte);
CropActivity.this.setResult(RESULT_OK,intent);

Bitmap photo = BitmapFactory.decodeByteArray(photobyte, 0, photobyte.length);
从字节数组,流 文件 可以加载进内存 bitmap

固定尺寸:不需要计算的此存压缩
//使用大图做尺寸压缩
BitmapFactory.Options options = new BitmapFactory.Options();
// // 从解码器中获取原始图片的宽高,这样避免了直接申请内存空间
// options.inJustDecodeBounds = true;
// Calculate inSampleSize
options.inSampleSize = 5;
// // 压缩完后便可以将inJustDecodeBounds设置为false了。
// options.inJustDecodeBounds = false;
options.inPreferredConfig = Bitmap.Config.RGB_565;
//做尺寸压缩
Log.e(“jxf”,”做尺寸压缩”);
Bitmap bitmap = BitmapFactory.decodeFile(outPutPhoto.getAbsolutePath(), options);

有关质量压缩:是压缩进本地文件:文件的质量:是本地的质量:和内存无关的

使用bitmap 外加各种条件来创建另一个 bitmap
Bitmap returnBm = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
注意:如果接下来就回收bitmap:在默写android版本中会造成null bitmap对象

方法解释:
一:
@param source The bitmap we are subsetting
* @param x The x coordinate of the first pixel in source
* @param y The y coordinate of the first pixel in source
* @param width The number of pixels in each row
* @param height The number of rows
* @param m Optional matrix to be applied to the pixels
* @param filter true if the source should be filtered.
* Only applies if the matrix contains more than just
* translation.
* @return A bitmap that represents the specified subset of source

public static Bitmap createBitmap(Bitmap source, int x, int y, int width, int height,
Matrix m, boolean filter) {
实例:做方向转换的:Bitmap returnBm = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
将获得角度传递给Matrix矩阵
Matrix matrix = new Matrix();
matrix.postRotate(degree);

二:
* @param width The width of the bitmap
* @param height The height of the bitmap
* @param config The bitmap config to create.
* @throws IllegalArgumentException if the width or height are <= 0
*/
public static Bitmap createBitmap(int width, int height, Config config) {
实例剪切图片的时候使用:主要用到config中的一些东西:例如图片质量
Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.RGB_565);///修改565

三:
* @param source The bitmap we are subsetting
* @param x The x coordinate of the first pixel in source
* @param y The y coordinate of the first pixel in source
* @param width The number of pixels in each row
* @param height The number of rows
*/
public static Bitmap createBitmap(Bitmap source, int x, int y, int width, int height) {
实例:绘制图片的一部分:
Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.RGB_565);///修改565
Log.e(“jxf”, “底层bitmap大小打印” + (bitmap.getRowBytes() * bitmap.getHeight()));
Canvas canvas = new Canvas(bitmap);
draw(canvas);
return Bitmap.createBitmap(bitmap, (getWidth() - borderlength) / 2, (getHeight() - borderlength) / 2, borderlength, borderlength);
四:

  • @param width The width of the bitmap
    • @param height The height of the bitmap
    • @param config The bitmap config to create.
      */
      public static Bitmap createBitmap(int width, int height, Config config) {
      实例:创建白纸:
      Bitmap copyBm = Bitmap.createBitmap(srcBm.getWidth(), srcBm.getHeight(), srcBm.getConfig());//config配置

在做尺寸压缩的时候:传递参数: options.inPreferredConfig = Bitmap.Config.RGB_565;
能够有效的压缩图片:因为:

A:透明度

R:红色

G:绿

B:蓝

Bitmap.Config ARGB_4444:每个像素占四位,即A=4,R=4,G=4,B=4,那么一个像素点占4+4+4+4=16位

Bitmap.Config ARGB_8888:每个像素占四位,即A=8,R=8,G=8,B=8,那么一个像素点占8+8+8+8=32位

Bitmap.Config RGB_565:每个像素占四位,即R=5,G=6,B=5,没有透明度,那么一个像素点占5+6+5=16位

Bitmap.Config ALPHA_8:每个像素占四位,只有透明度,没有颜色。

一般情况下我们都是使用的ARGB_8888,由此可知它是最占内存的,因为一个像素占32位,8位=1字节,所以一个像素占4字节的内存。
假设有一张480x800的图片,如果格式为ARGB_8888,那么将会占用1500KB的内存。

有关画笔、画布和矩阵:
//创建与原图大小一致的空白bitmap:纸
Bitmap copyBm = Bitmap.createBitmap(srcBm.getWidth(), srcBm.getHeight(), srcBm.getConfig());//config配置
//定义画笔
Paint paint = new Paint();
//把纸铺在画版上
Canvas canvas = new Canvas(copyBm);
//把srcBm的内容绘制在copyBm上
canvas.drawBitmap(srcBm, new Matrix(), paint);// new Matrix(),矩阵对象:可以对图片进行一些列的操作(canvas.drawBitmap(bmSrc, matrix, paint);)
//画完:原来为白纸的copyBm 就有东西了 和原来一样:
Matrix:可以倒影啊 伸缩啊 拉伸啊 一些列的效果 等等

bitmap压缩的3种方法:
http://www.tuicool.com/articles/vEf6Nf
有关压缩的个人相关
最近在研究微信的sdk,在缩略图这遇到了一点问题。

微信的缩略图要求是不大于32k,这就需要对我的图片进行压缩。试了几种方法,一一道来。

代码如下

    ByteArrayOutputStream baos =  new  ByteArrayOutputStream();  
    image.compress(Bitmap.CompressFormat.JPEG,  100 , baos);
     int  options =  100 ;  
     while  ( baos.toByteArray().length /  1024 > 32 ) {  
        baos.reset();
        image.compress(Bitmap.CompressFormat.JPEG, options, baos);
        options -=  10 ;
    }  
    ByteArrayInputStream isBm =  new  ByteArrayInputStream(baos.toByteArray());  
    Bitmap bitmap = BitmapFactory.decodeStream(isBm,  null ,  null );

最开始使用这个来进行压缩,但是始终压缩不到32k这么小。后来看高手的解释才明白,
这种压缩方法之所以称之为质量压缩,是因为它不会减少图片的像素。它是在保持像素的前提下改变图片的位深及透明度等,
来达到压缩图片的目的。进过它压缩的图片文件大小会有改变,但是导入成bitmap后占得内存是不变的。因为要保持像素不变,
所以它就无法无限压缩,到达一个值之后就不会继续变小了。显然这个方法并不适用与缩略图,其实也不适用于想通过压缩图片减少内存的适用,
仅仅适用于想在保证图片质量的同时减少文件大小的情况而已。
2、采样率压缩法:

代码如下

    ByteArrayOutputStream out = new ByteArrayOutputStream();

image.compress(Bitmap.CompressFormat.JPEG, 100, out);
BitmapFactory.Options newOpts = new BitmapFactory.Options();
int be = 2;
newOpts.inSampleSize = be;
ByteArrayInputStream isBm = new ByteArrayInputStream(out.toByteArray());
Bitmap bitmap = BitmapFactory.decodeStream(isBm, null , null );
第二个使用的是这个方法,可以将图片压缩到足够小,但是也有一些问题。因为采样率是整数,
所以不能很好的保证图片的质量。如我们需要的是在2和3采样率之间,用2的话图片就大了一点,
但是用3的话图片质量就会有很明显的下降。这样也无法完全满足我的需要。不过这个方法的好处是大大的缩小了内存的使用,
在读存储器上的图片时,如果不需要高清的效果,可以先只读取图片的边,通过宽和高设定好取样率后再加载图片,这样就不会过多的占用内存。如下

    BitmapFactory.Options newOpts =  new  BitmapFactory.Options();    
    newOpts.inJustDecodeBounds =  true ;  
    Bitmap bitmap = BitmapFactory.decodeFile(path,newOpts);
    newOpts.inJustDecodeBounds =  false ;  
     int  w = newOpts.outWidth;  
     int  h = newOpts.outHeight;  
    //计算出取样率
    newOpts.inSampleSize = be;
    bitmap = BitmapFactory.decodeFile(srcPath, newOpts);  

这样的好处是不会先将大图片读入内存,大大减少了内存的使用,也不必考虑将大图片读入内存后的释放事宜。
3、缩放法:

以上俩个方法都无法满足要求,只好考虑用缩放来实现。本不想用这种方法来实现,不过网上看到的方法基本都是上面俩种。
缩放法其实很简单,设定好matrix,在createBitmap就可以了。但是我们并不知道缩放比例,而是要求了图片的最终大小。
直接用大小的比例来做的话肯定是有问题的,用大小比例的开方来做会比较接近,但是还是有差距。但是只要再做一下微调应该就可以了,
微调的话就是修改过的图片大小比最终大小还大的话,就进行0.8的压缩再比较,循环直到大小合适。这样就能得到合适大小的图片,而且也能比较保证质量。代码如下

ByteArrayOutputStream out = new ByteArrayOutputStream();
image.compress(Bitmap.CompressFormat.JPEG, 85, out);
float zoom = (float)Math.sqrt(size * 1024 / (float)out.toByteArray().length);

    Matrix matrix = new Matrix();
    matrix.setScale(zoom, zoom);


    Bitmap result = Bitmap.createBitmap(image, 0, 0, image.getWidth(), image.getHeight(), matrix, true);


    out.reset();
    result.compress(Bitmap.CompressFormat.JPEG, 85, out);
    while(out.toByteArray().length > size * 1024){
        System.out.println(out.toByteArray().length);
        matrix.setScale(0.9f, 0.9f);
        result = Bitmap.createBitmap(result, 0, 0, result.getWidth(), result.getHeight(), matrix, true);
        out.reset();
        result.compress(Bitmap.CompressFormat.JPEG, 85, out);
    } 

关于options参数的问题: injustdecodebounds这个表示将不将图片加载进内存当中的方法 :如果这个是个true 表示只加载图片的宽高,没有图片加载进内存:设置成true:通过option得到宽高的参数:
所以:如果是固定的尺寸压缩:就没有必要设置这个。但是设置完true:要做图片显示的时候:要将这个参数设置成false

得到屏幕的宽高:
diaplay对象=getwindowsmanager().getdefaultdiaplay();
diaplay对象.getwidth()/getheigeht()得到屏幕的宽高
Options对象=new options()对象
Options对象.injustdecodebounds(true):表示使用这个Options对象加载图片的时候只得图片的宽高:图片没有一加载进内存:得到的图片是null
BitmapFactory.decodeFile(“sdcard/dog.jpg”, Options对象);
Options对象.getwidth();得到超过屏幕尺寸超过内存的图片尺寸
照片尺寸与屏幕的比较:得到比例
//设置缩放比例
opts.inSampleSize = scale
再将Options对象.injustdecodebounds(false)置为false
比例也已经设置BitmapFactory.decodeFile(“sdcard/dog.jpg”, Options对象)得到图片加载进内存:照片的内存大小改变

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值