做项目总会遇到 bitmap这个好东西,稍不注意 则会内存溢出,解决办法 有压缩质量,和及时回收两个法子


下面结合 私人笔记 以及查询了一些网上资料,补充升级,,针对图片压缩所作的总结而言,有复习提高的作用,
供各位 取长补短,及时查阅温习。
11/3 update 代码版总结

一.图片的存在形式


1.文件file形式(即以二进制形式存在于硬盘上) 获取大小(Byte):File.length()
2.流stream的形式(即以二进制形式存在于内存中)
获取大小(Byte):new FileInputStream(File).available() 3.Bitmap形式 获取大小(Byte):Bitmap.getByteCount() 4.字符串string形式[查询多方资料,确认没有人写这一条,来自本人很久之前的总结] 获取大小 String.length();

这四种形式的区别:文件形式,字符串和流的形式对图片体积大小并没有影响,也就是说,如果你手机SD卡上的如果是100K,那 么通过流的形式读到内存中,也一定是占100K的内存,注意是流的形式,不是Bitmap的形式,当图片以Bitmap的形式存在时,其占用的内存会瞬间 变大,
我试过500K文件形式的图片加载到内存,以Bitmap形式存在时,占用内存将近10M,当然这个增大的倍数并不是固定的

附上我常用工具类中 图片转字符串的代码,之前的项目因特殊需求 上传下载获取的图片 都是 base64加密解密的,so印象深刻。


/** * 对图片进行Base64加密 *  * @param bitmap *            位图文件 * @return 加密代码 */public static String bitmap2Base64(Bitmap bitmap) {   if (bitmap == null) {      return null;   }   ByteArrayOutputStream stream = new ByteArrayOutputStream();   bitmap.compress(Bitmap.CompressFormat.JPEG, NORMAL_QUALITY, stream);   byte[] byteArray = stream.toByteArray();   String base64Code = Base64.encodeToString(byteArray, Base64.DEFAULT);   return base64Code;}


加密都给了,解密也抛出来吧


/** * 对Base64加密图片进行解密 *  * @param base64Data * @return */public static Bitmap base642Bitmap(String base64Data) {   byte[] bytes = Base64.decode(base64Data, Base64.DEFAULT);   return BitmapFactory.decodeByteArray(bytes, 0, bytes.length);}


压缩入门 :质量压缩和尺寸压缩

质 量压缩,它其实只能实现对file的影响,你可以把一个file转成bitmap再转成file,或者直接将一个bitmap转成file时,这个最终的 file是被压缩过的,但是中间的bitmap并没有被压缩(或者说几乎没有被压缩,我不确定),因为bigmap在内存中的大小是按像素计算的,对于质量压缩,并不会改变图片的像素,所以就算质量被压缩了,但是bitmap在内存的占有率还是没变小,但你做成file时,它确实变小 了;

而尺寸压缩由于是减小了图片的像素,所以它直接对bitmap产生了影响,当然最终的file也是相对的变小了;


当图片以Bitmap形式存在时,占用的内存就大的多了, 为什么 呢,首先我们需要知道Bitmap大小计算的方式 bitmap大小=图片长度(px)*图片宽度(px)*单位像素占用的字节数

引入下面的内容


二.常见的压缩方式


1. 将图片保存到本地时进行压缩, 即将图片从Bitmap形式变为File形式时进行压缩,

特点是: File形式的图片确实被压缩了, 但是当你重新读取压缩后的file为 Bitmap是,它占用的内存并没有改变

  1. publicstaticvoidcompressBmpToFile(Bitmapbmp,Filefile){
  2. ByteArrayOutputStreambaos=newByteArrayOutputStream();
  3. intoptions=80;//个人喜欢从80开始,
  4. bmp.compress(Bitmap.CompressFormat.JPEG,options,baos);
  5. while(baos.toByteArray().length/1024>100){
  6. baos.reset();
  7. options-=10;
  8. bmp.compress(Bitmap.CompressFormat.JPEG,options,baos);
  9. }
  10. try{
  11. FileOutputStreamfos=newFileOutputStream(file);
  12. fos.write(baos.toByteArray());
  13. fos.flush();
  14. fos.close();
  15. }catch(Exceptione){
  16. e.printStackTrace();
  17. }
  18. }

方法说明:该方法是压缩图片的质量, 注意它不会减少图片的像素,比方说, 你的图片是300K的, 1280*700像素的, 经过该方法压缩后, File形式的图片是在100以下, 以方便上传服务器, 但是你BitmapFactory.decodeFile到内存中,变成Bitmap时,它的像素仍然是1280*700, 计算图片像素的方法是bitmap.getWidth()和bitmap.getHeight(), 图片是由像素组成的, 每个像素又包含什么呢? 熟悉PS的人知道, 图片是有色相,明度和饱和度构成的.
该方法的官方文档也解释说, 它会让图片重新构造, 但是有可能图像的位深(即色深)和每个像素的透明度会变化,JPEG onlysupports opaque(不透明), 也就是说以jpeg格式压缩后, 原来图片中透明的元素将消失.所以这种格式很可能造成失真
既然它是改变了图片的显示质量, 达到了对File形式的图片进行压缩, 图片的像素没有改变的话, 那重新读取经过压缩 的file为Bitmap时, 它占用的内存并不会少.(不相信的可以试试)
因为: bitmap.getByteCount() 是计算它的像素所占用的内存, 请看官方解释:Returns the number of bytes used to store this bitmap's pixels.

2. 将图片从本地读到内存时,进行压缩 ,即图片从File形式变为Bitmap形式

特点: 通过设置采样率, 减少图片的像素, 达到对内存中的Bitmap进行压缩

先看一个方法:

该方法是对内存中的Bitmap进行质量上的压缩, 由上面的理论可以得出该方法是无效的, 而且也是没有必要的, 因为你已经将它读到内存中了,再压缩多此一举, 尽管在获取系统相册图片时,某些手机会直接返回一个Bitmap, 但是这种情况下, 返回的Bitmap都是经过压缩的, 它不可能直接返回一个原声的Bitmap形式的图片, 后果可想而知 [java] view plain copy
  1. privateBitmapcompressBmpFromBmp(Bitmapimage){
  2. ByteArrayOutputStreambaos=newByteArrayOutputStream();
  3. intoptions=100;
  4. image.compress(Bitmap.CompressFormat.JPEG,100,baos);
  5. while(baos.toByteArray().length/1024>100){
  6. baos.reset();
  7. options-=10;
  8. image.compress(Bitmap.CompressFormat.JPEG,options,baos);
  9. }
  10. ByteArrayInputStreamisBm=newByteArrayInputStream(baos.toByteArray());
  11. Bitmapbitmap=BitmapFactory.decodeStream(isBm,null,null);
  12. returnbitmap;


再看一个方法:

[java] view plain copy
  1. privateBitmapcompressImageFromFile(StringsrcPath){
  2. BitmapFactory.OptionsnewOpts=newBitmapFactory.Options();
  3. newOpts.inJustDecodeBounds=true;//只读边,不读内容
  4. Bitmapbitmap=BitmapFactory.decodeFile(srcPath,newOpts);
  5. newOpts.inJustDecodeBounds=false;
  6. intw=newOpts.outWidth;
  7. inth=newOpts.outHeight;
  8. floathh=800f;//
  9. floatww=480f;//
  10. intbe=1;
  11. if(w>h&&w>ww){
  12. be=(int)(newOpts.outWidth/ww);
  13. }elseif(w<h&&h>hh){
  14. be=(int)(newOpts.outHeight/hh);
  15. }
  16. if(be<=0)
  17. be=1;
  18. newOpts.inSampleSize=be;//设置采样率
  19. newOpts.inPreferredConfig=Config.ARGB_8888;//该模式是默认的,可不设
  20. newOpts.inPurgeable=true;//同时设置才会有效
  21. newOpts.inInputShareable=true;//。当系统内存不够时候图片自动被回收
  22. bitmap=BitmapFactory.decodeFile(srcPath,newOpts);
  23. //returncompressBmpFromBmp(bitmap);//原来的方法调用了这个方法企图进行二次压缩
  24. //其实是无效的,大家尽管尝试
  25. returnbitmap;
  26. }


方法说明: 该方法就是对Bitmap形式的图片进行压缩, 也就是通过设置采样率, 减少Bitmap的像素, 从而减少了它所占用的内存


update


图片不同存在形式占用内存 大小计算


引用参考较多的图片压缩总结

三.代码总结

1.质量压缩

    private Bitmap compressImage(Bitmap image) {                    ByteArrayOutputStream baos = new ByteArrayOutputStream();              image.compress(Bitmap.CompressFormat.JPEG, 100, baos);//质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中              int options = 100;              while ( baos.toByteArray().length / 1024>100) {  //循环判断如果压缩后图片是否大于100kb,大于继续压缩                         baos.reset();//重置baos即清空baos                  image.compress(Bitmap.CompressFormat.JPEG, options, baos);//这里压缩options%,把压缩后的数据存放到baos中                  options -= 10;//每次都减少10              }              ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());//把压缩后的数据baos存放到ByteArrayInputStream中              Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);//把ByteArrayInputStream数据生成图片              return bitmap;          }  

2.按比例压缩路径形式的图片


    private Bitmap getimage(String srcPath) {              BitmapFactory.Options newOpts = new BitmapFactory.Options();              //开始读入图片,此时把options.inJustDecodeBounds 设回true了              newOpts.inJustDecodeBounds = true;              Bitmap bitmap = BitmapFactory.decodeFile(srcPath,newOpts);//此时返回bm为空                            newOpts.inJustDecodeBounds = false;              int w = newOpts.outWidth;              int h = newOpts.outHeight;              //现在主流手机比较多是800*480分辨率,所以高和宽我们设置为              float hh = 800f;//这里设置高度为800f              float ww = 480f;//这里设置宽度为480f              //缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可              int be = 1;//be=1表示不缩放              if (w > h && w > ww) {//如果宽度大的话根据宽度固定大小缩放                  be = (int) (newOpts.outWidth / ww);              } else if (w < h && h > hh) {//如果高度高的话根据宽度固定大小缩放                  be = (int) (newOpts.outHeight / hh);              }              if (be <= 0)                  be = 1;              newOpts.inSampleSize = be;//设置缩放比例              //重新读入图片,注意此时已经把options.inJustDecodeBounds 设回false了              bitmap = BitmapFactory.decodeFile(srcPath, newOpts);              return compressImage(bitmap);//压缩好比例大小后再进行质量压缩          }  


3.图片按比例进行尺寸压缩


    private Bitmap comp(Bitmap image) {                    ByteArrayOutputStream baos = new ByteArrayOutputStream();                 image.compress(Bitmap.CompressFormat.JPEG, 100, baos);          if( baos.toByteArray().length / 1024>1024) {//判断如果图片大于1M,进行压缩避免在生成图片(BitmapFactory.decodeStream)时溢出                baos.reset();//重置baos即清空baos              image.compress(Bitmap.CompressFormat.JPEG, 50, baos);//这里压缩50%,把压缩后的数据存放到baos中          }          ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());          BitmapFactory.Options newOpts = new BitmapFactory.Options();          //开始读入图片,此时把options.inJustDecodeBounds 设回true了          newOpts.inJustDecodeBounds = true;          Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, newOpts);          newOpts.inJustDecodeBounds = false;          int w = newOpts.outWidth;          int h = newOpts.outHeight;          //现在主流手机比较多是1280*720分辨率,所以高和宽我们设置为          float hh = 360f;//这里设置高度为360f          float ww = 360f;//这里设置宽度为360f          //缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可          int be = 1;//be=1表示不缩放          if (w > h && w > ww) {//如果宽度大的话根据宽度固定大小缩放              be = (int) (newOpts.outWidth / ww);          } else if (w < h && h > hh) {//如果高度高的话根据宽度固定大小缩放              be = (int) (newOpts.outHeight / hh);          }          if (be <= 0)              be = 1;          newOpts.inSampleSize = be;//设置缩放比例          //重新读入图片,注意此时已经把options.inJustDecodeBounds 设回false了          isBm = new ByteArrayInputStream(baos.toByteArray());          bitmap = BitmapFactory.decodeStream(isBm, null, newOpts);          return compressImage(bitmap);//压缩好比例大小后再进行质量压缩      }  



更多相关文章

  1. Android(安卓)适配语言、图片、横竖屏、不同版本
  2. Android高仿微信图片多选功能
  3. Android(安卓)显示GIF图片实例详解
  4. android点击本地缩略图,弹出显示服务器原图
  5. android:ListView滑动时图片重复加载,多次调用了GetView().
  6. android 实现从网络上抓取图片并显示在手机上
  7. Android使用ViewPager实现图片轮播(高度自适应,左右循环,自动轮播)
  8. Android——UI篇:android视频压缩:兼容7.0版本。
  9. Android(安卓)兼容Android(安卓)7拍摄照片/打开相册/选择照片/剪

随机推荐

  1. Android实现手机静音
  2. android 应用程序退出
  3. Android基础之四大组件---Activity
  4. Android开发之Sqlite的使用
  5. Android(安卓)PopupWindow Dialog 关于 i
  6. Exoplayer2实现边播放边缓存
  7. ANDROID L——Material Design详解(视图和
  8. android的input_event的流程,以及如何获取
  9. 聚焦 Android 11: Jetpack
  10. Android开发_在Android Studio中搜索项目