详解android 通过uri获取bitmap图片并压缩

很多人在调用图库选择图片时会在onActivityResult中用Media.getBitmap来获取返回的图片,如下:

Uri mImageCaptureUri = data.getData();Bitmap photoBmp = null;if (mImageCaptureUri != null) {  photoBmp = MediaStore.Images.Media.getBitmap(ac.getContentResolver(), mImageCaptureUri);}

但是Media.getBitmap这个方法获取已知uri图片的方式并不可取,咱来看看Media.getBitmap()方法的源码:

public static final Bitmap getBitmap(ContentResolver cr, Uri url)    throws FileNotFoundException, IOException {  InputStream input = cr.openInputStream(url);  Bitmap bitmap = BitmapFactory.decodeStream(input);  input.close();  return bitmap;}

其实它很简单很粗暴,返回的是原始大小的bitmap,当图库选择的图片很大时程序极有可能会报OOM。

为了避免OOM,咱们需要改进该方法,在 BitmapFactory.decodeStream 之前压缩图片,以下是我改进后的代码:

在onActivityResult中调用

Uri mImageCaptureUri = data.getData(); Bitmap photoBmp = null; if (mImageCaptureUri != null) { photoBmp = getBitmapFormUri(ac, mImageCaptureUri); }
/**   * 通过uri获取图片并进行压缩   *   * @param uri   */  public static Bitmap getBitmapFormUri(Activity ac, Uri uri) throws FileNotFoundException, IOException {    InputStream input = ac.getContentResolver().openInputStream(uri);    BitmapFactory.Options onlyBoundsOptions = new BitmapFactory.Options();    onlyBoundsOptions.inJustDecodeBounds = true;    onlyBoundsOptions.inDither = true;//optional    onlyBoundsOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;//optional    BitmapFactory.decodeStream(input, null, onlyBoundsOptions);    input.close();    int originalWidth = onlyBoundsOptions.outWidth;    int originalHeight = onlyBoundsOptions.outHeight;    if ((originalWidth == -1) || (originalHeight == -1))      return null;    //图片分辨率以480x800为标准    float hh = 800f;//这里设置高度为800f    float ww = 480f;//这里设置宽度为480f    //缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可    int be = 1;//be=1表示不缩放    if (originalWidth > originalHeight && originalWidth > ww) {//如果宽度大的话根据宽度固定大小缩放      be = (int) (originalWidth / ww);    } else if (originalWidth < originalHeight && originalHeight > hh) {//如果高度高的话根据宽度固定大小缩放      be = (int) (originalHeight / hh);    }    if (be <= 0)      be = 1;    //比例压缩    BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();    bitmapOptions.inSampleSize = be;//设置缩放比例    bitmapOptions.inDither = true;//optional    bitmapOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;//optional    input = ac.getContentResolver().openInputStream(uri);    Bitmap bitmap = BitmapFactory.decodeStream(input, null, bitmapOptions);    input.close();     return compressImage(bitmap);//再进行质量压缩  } 
/**  * 质量压缩方法  *  * @param image  * @return  */  public static 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      //第一个参数 :图片格式 ,第二个参数: 图片质量,100为最高,0为最差 ,第三个参数:保存压缩后的数据的流      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;  }

OOM的问题解决了,但是又碰到另外一个问题,用三星手机拍照或者选择照片后返回来的图片居然转了90度。。苦逼的android程序员。。接着改。。

讲onActivityResult中的代码进行改进:

Uri originalUri = null;   File file = null;   if (null != data && data.getData() != null) {     originalUri = data.getData();     file = getFileFromMediaUri(ac, originalUri);   } Bitmap photoBmp = getBitmapFormUri(ac, Uri.fromFile(file)); int degree = getBitmapDegree(file.getAbsolutePath()); /**  * 把图片旋转为正的方向  */ Bitmap newbitmap = rotateBitmapByDegree(photoBmp, degree);
/**   * 通过Uri获取文件   * @param ac   * @param uri   * @return   */  public static File getFileFromMediaUri(Context ac, Uri uri) {    if(uri.getScheme().toString().compareTo("content") == 0){      ContentResolver cr = ac.getContentResolver();      Cursor cursor = cr.query(uri, null, null, null, null);// 根据Uri从数据库中找      if (cursor != null) {        cursor.moveToFirst();        String filePath = cursor.getString(cursor.getColumnIndex("_data"));// 获取图片路径        cursor.close();        if (filePath != null) {          return new File(filePath);        }      }    }else if(uri.getScheme().toString().compareTo("file") == 0){      return new File(uri.toString().replace("file://",""));    }      return null;    }
/**   * 读取图片的旋转的角度   *   * @param path 图片绝对路径   * @return 图片的旋转角度   */  public static int getBitmapDegree(String path) {    int degree = 0;    try {      // 从指定路径下读取图片,并获取其EXIF信息      ExifInterface exifInterface = new ExifInterface(path);      // 获取图片的旋转信息      int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION,          ExifInterface.ORIENTATION_NORMAL);      switch (orientation) {        case ExifInterface.ORIENTATION_ROTATE_90:          degree = 90;          break;        case ExifInterface.ORIENTATION_ROTATE_180:          degree = 180;          break;        case ExifInterface.ORIENTATION_ROTATE_270:          degree = 270;          break;      }    } catch (IOException e) {      e.printStackTrace();    }    return degree;  }
/**   * 将图片按照某个角度进行旋转   *   * @param bm   需要旋转的图片   * @param degree 旋转角度   * @return 旋转后的图片   */  public static Bitmap rotateBitmapByDegree(Bitmap bm, int degree) {    Bitmap returnBm = null;     // 根据旋转角度,生成旋转矩阵    Matrix matrix = new Matrix();    matrix.postRotate(degree);    try {      // 将原始图片按照旋转矩阵进行旋转,并得到新的图片      returnBm = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true);    } catch (OutOfMemoryError e) {    }    if (returnBm == null) {      returnBm = bm;    }    if (bm != returnBm) {      bm.recycle();    }    return returnBm;  }

如有疑问请留言或到本站社区交流讨论,感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

更多相关文章

  1. “罗永浩抖音首秀”销售数据的可视化大屏是怎么做出来的呢?
  2. Nginx系列教程(三)| 一文带你读懂Nginx的负载均衡
  3. 不吹不黑!GitHub 上帮助人们学习编码的 12 个资源,错过血亏...
  4. Android(安卓)- Fragment,View动画,组合动画,属性动画
  5. Android使用SharedPreferences存储数据的实现方法
  6. android LruCache 有效避免程序OOM
  7. Android[中级教程]第四章 单元测试AndroidTestCase
  8. Android之intent传值的三种方法
  9. Android(安卓)Content Provider的共享数据更新通知机制分析

随机推荐

  1. 什么,3行Python代码就能获取海量数据?
  2. 2020 年,我的年终总结!时间与人生(下)
  3. 第一天学习C语言
  4. 华为交换机console口设置密码及状态查看
  5. 自学第五十二天
  6. 华为交换机开启Telnet服务及修改端口
  7. 【前端词典】Vuex 注入 Vue 生命周期的过
  8. 初次见面,请多关注
  9. 写独立个人博客,还有机会吗?
  10. 【前端词典】如何向老板解释反向代理