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

详解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;
  }

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

时间: 2017-10-22

Android学习笔记之ContentProvider和Uri详解

本文介绍了自定义Content Provider的相关内容,完全解析内容提供者的用法.Content Provider,内容提供者,相信大家对这个组件的名字都不陌生,可能是自己平时做的都是一些简单的App,所以对于Content Provider的使用并不是很多,也不是特别熟悉.但是这里还是对Content Provider作个简单的总结,不是很深入,但是希望能给包括我在内的初学者一点帮助,看完这篇能对这个组件有个总体上的了解. 一.使用ContentProvider(内容提供者)共享数据 Co

适配android7.0获取文件的Uri的方法

前言# Android 7.0已经发布很久了,虽然市场份额还不是很高,但是流行起来都是早晚的事,所以适配Android 7.0刻不容缓. Android 7.0 对系统进行了很多的优化:例如文件访问权限,省电,网络,后台等等,其中最突出的就是应用外的Uri访问. 什么时候会用到Uri的应用外访问呢?举一个简单的例子,下载apk更新,这个时候会调用系统功能来安装这个apk,这就是应用外访问文件,需要传入文件的Uri. 但是这样可能会显得不太安全,万一是什么非常重要的文件就糟糕了,所以Android

android图片文件的路径地址与Uri的相互转换方法

一个android文件的Uri地址一般如下: content://media/external/images/media/62026 这是一张图片的Uri,那么我们如何根据这个Uri获得其在文件系统中的路径呢? 其实很简单,直接上代码: public static String getRealFilePath( final Context context, final Uri uri ) { if ( null == uri ) return null; final String scheme

android实现Uri获取真实路径转换成File的方法

本文实例讲述了android实现Uri获取真实路径转换成File的方法.分享给大家供大家参考.具体实现方法如下: Uri uri = data.getData(); String[] proj = { MediaStore.Images.Media.DATA }; Cursor actualimagecursor = managedQuery(uri,proj,null,null,null); int actual_image_column_index = actualimagecursor.g

Android使用WebView.loadUri()打开网页的方法

本文实例讲述了Android使用WebView.loadUri()打开网页的方法.分享给大家供大家参考,具体如下: 程序如下所示: import android.app.Activity; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.webkit.WebView; import android.widget.EditText

Android Uri和文件路径互相转换的实例代码

在项目中需要用到将Uri转换为绝对路径,在网上找到一个方法,做个笔记 网上有不少方法,但是有的对4.4后的版本无效,这里的方法可以在4.4之后的版本将Uri转换为绝对路径 public class GetPathFromUri { /** * 专为Android4.4设计的从Uri获取文件绝对路径 */ public static String getPath(final Context context, final Uri uri) { final boolean isKitKat = Bui

Android 7.0行为变更 FileUriExposedException解决方法

Android 7.0行为变更 FileUriExposedException解决方法 当我们开发关于[在应用间共享文件]相关功能的时候,在Android 7.0上经常会报出此运行时异常,那么Android 7.0以下没问题的代码,为什么跑到Android 7.0+的设备上运行就出问题了呢?,这主要来自于Android 7.0的一项[行为变更]! 对于面向 Android 7.0 的应用,Android 框架执行的 StrictMode API 政策禁止在您的应用外部公开 file:// URI

Android Intent调用 Uri的方法总结

Android Intent调用 Uri的方法总结 //调用浏览器 Uri uri = Uri.parse(""); Intent it = new Intent(Intent.ACTION_VIEW,uri); startActivity(it); //显示某个坐标在地图上 Uri uri = Uri.parse("geo:38.899533,-77.036476"); Intent it = new Intent(Intent.Action_VIEW,uri);

Android intent之间复杂参数传递方法详解

本文详细讲述了Android intent之间复杂参数传递方法.分享给大家供大家参考,具体如下: Intent是Activity与Activity之间,Activity与Service之间传递参数的介质,而这两种通常实现的是Java基本对象类型和String的传递. 在实际项目中,页面之间传值,除了以上几种,经常还有传递Object对象.List类型.List<Object>类型和全局变量等等的需求.本文就是介绍怎么传递这几种类型的参数. 一.传递List<String>和List

Android实现调用震动的方法

本文实例讲述了Android实现调用震动的方法.分享给大家供大家参考,具体如下: 调用Android系统的震动,只需要一个类 那就是Vibrator ,这个类在hard包中,一看系统级的服务,又要通过manifest.xml文件设置权限了 <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/an

Android开发调用WebService的方法示例

本文实例讲述了Android开发调用WebService的方法.分享给大家供大家参考,具体如下: WebService是一种基于SOAP协议的远程调用标准,通过webservice可以将不同操作系统平台.不同语言.不同技术整合到一块.在Android SDK中并没有提供调用WebService的库,因此,需要使用第三方的SDK来调用WebService.PC版本的WEbservice客户端库非常丰富,例如Axis2,CXF等,但这些开发包对于Android系统过于庞大,也未必很容易移植到Andr

Android 通过Intent调用系统拍照程序出现图片太小的问题解决办法

Android 通过Intent调用系统拍照程序出现图片太小的问题解决办法 Intent it = newIntent("android.media.action.IMAGE_CAPTURE"); startActivityForResult(it, Activity.DEFAULT_KEYS_DIALER); 按下拍照键后,会返回到你的activity,所以你的activity要在onActivityResult方法里加一个处理, protectedvoidonActivityRes

Android非XML形式动态生成、调用页面的方法

本文实例讲述了Android非XML形式动态生成.调用页面的方法.分享给大家供大家参考.具体分析如下: 这个问题是这样的:我们不使用XML构建页面,而是使用程序构建新的页面,并在页面中添加各种控件. 一.构建新页面: ① 在src目录中添加一个class,命名为SignPage ② 在AndroidMainfest.xml中添加一个Activity: <activity android:name="com.example.stest.SignPage"/> 注意,这句添加后

Android编程实现全局获取Context及使用Intent传递对象的方法详解

本文实例讲述了Android编程实现全局获取Context及使用Intent传递对象的方法.分享给大家供大家参考,具体如下: 一.全局获取 Context Android 开发中很多地方需要用到 Context,比如弹出 Toast.启动活动.发送广播.操作数据库-- 由于很多操作都是在活动中进行的,而活动本身就是一个 Context 对象,所以获取 Context 并不是那么困难. 但是,当应用程序的架构逐渐开始复杂起来的时候,很多的逻辑代码都将脱离 Activity 类,由此在某些情况下,获

Android获取手机通讯录、sim卡联系人及调用拨号界面方法

android获取手机通讯录联系人信息 复制代码 代码如下: private void getPhoneContacts() {        ContentResolver resolver = this.getContentResolver();                // 获取手机联系人       Cursor phoneCursor = resolver.query(Phone.CONTENT_URI,                  new String[] { Phone

Android编程使用Intent传递图片的方法详解

本文实例讲述了Android编程使用Intent传递图片的方法.分享给大家供大家参考,具体如下: 基本思路是先把bitmap转化为byte数组,用Intent传递数组,在将数组转化为bitmap bitmap转化为byte数组的方法: private byte[] Bitmap2Bytes(Bitmap bm){ ByteArrayOutputStream baos = new ByteArrayOutputStream(); bm.compress(Bitmap.CompressFormat.