在Android 编程中经常会用到Uri转化为文件路径,如我们从相册选择图片上传至服务器,一般上传前需要对图片进行压缩,这时候就要用到图片的绝对路径。
下面对我开发中uri转path路径遇到的问题进行总结,其中涉及到Android不同api下对于uri的处理,还有对于Google相册图片该如何获取其图片路径。
1. 从相册获取图片
我们从相册获取的图片的代码如下:
// 激活系统图库,选择一张图片 Intent intent = new Intent(Intent.ACTION_PICK); intent.setType("image/*"); intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); startActivityForResult(intent, Constants.PHOTO_REQUEST_GALLERY);
当然针对Android 6.0以上系统还需要获取WRITE_EXTERNAL_STORAGE和READ_EXTERNAL_STORAGE操作手机sd卡权限才行。
然后再在onActivityResult中获取到图片的uri路径:
@Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == Activity.RESULT_OK) { switch (requestCode) { case Constants.PHOTO_REQUEST_GALLERY: Uri uri = data.getData(); break; } } }
Uri:通用资源标志符(Universal Resource Identifier, 简称”URI”),Uri代表要操作的数据,Android上可用的每种资源(如图像、视频片段等)都可以用Uri来表示。
Uri一般由三部分组成:访问资源的命名机制。存放资源的主机名。资源自身的名称,由路径表示。
2.根据Uri获取path路径
2.1 Android 4.4以下获取图片路径
/** * 获取小于api19时获取相册中图片真正的uri * 对于路径是:content://media/external/images/media/33517这种的,需要转成/storage/emulated/0/DCIM/Camera/IMG_20160807_133403.jpg路径,也是使用这种方法 * @param context * @param uri * @return */ public static String getFilePath_below19(Context context,Uri uri) { //这里开始的第二部分,获取图片的路径:低版本的是没问题的,但是sdk>19会获取不到 Cursor cursor = null; String path = ""; try { String[] proj = {MediaStore.Images.Media.DATA}; //好像是android多媒体数据库的封装接口,具体的看Android文档 cursor = context.getContentResolver().query(uri, proj, null, null, null); //获得用户选择的图片的索引值 int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); //将光标移至开头 ,这个很重要,不小心很容易引起越界 cursor.moveToFirst(); //最后根据索引值获取图片路径 结果类似:/mnt/sdcard/DCIM/Camera/IMG_20151124_013332.jpg path = cursor.getString(column_index); } finally { if (cursor != null) { cursor.close(); } } return path; }
2.2.2 对于Android 4.4及以上机型获取path
/** * @param context 上下文对象 * @param uri 当前相册照片的Uri * @return 解析后的Uri对应的String */ @SuppressLint("NewApi") public static String getPath(final Context context, final Uri uri) { final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; String pathHead = "file:///"; // 1. DocumentProvider if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) { // 1.1 ExternalStorageProvider if (isExternalStorageDocument(uri)) { final String docId = DocumentsContract.getDocumentId(uri); final String[] split = docId.split(":"); final String type = split[0]; if ("primary".equalsIgnoreCase(type)) { return pathHead + Environment.getExternalStorageDirectory() + "/" + split[1]; } } // 1.2 DownloadsProvider else if (isDownloadsDocument(uri)) { final String id = DocumentsContract.getDocumentId(uri); final Uri contentUri = ContentUris. withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(id)); return pathHead + getDataColumn(context, contentUri, null, null); } // 1.3 MediaProvider else if (isMediaDocument(uri)) { final String docId = DocumentsContract.getDocumentId(uri); final String[] split = docId.split(":"); final String type = split[0]; Uri contentUri = null; if ("image".equals(type)) { contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; } else if ("video".equals(type)) { contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; } else if ("audio".equals(type)) { contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; } final String selection = "_id=?"; final String[] selectionArgs = new String[]{split[1]}; return pathHead + getDataColumn(context, contentUri, selection, selectionArgs); } } // 2. MediaStore (and general) else if ("content".equalsIgnoreCase(uri.getScheme())) { if (isGooglePhotosUri(uri)) {//判断是否是google相册图片 return uri.getLastPathSegment(); } else if (isGooglePlayPhotosUri(uri)) {//判断是否是Google相册图片 return getImageUrlWithAuthority(context, uri); } else {//其他类似于media这样的图片,和android4.4以下获取图片path方法类似 return getFilePath_below19(context, uri); } } // 3. 判断是否是文件形式 File else if ("file".equalsIgnoreCase(uri.getScheme())) { return pathHead + uri.getPath(); } return null; } /** * @param uri * The Uri to check. * @return Whether the Uri authority is ExternalStorageProvider. */ private static boolean isExternalStorageDocument(Uri uri) { return "com.android.externalstorage.documents".equals(uri.getAuthority()); } /** * @param uri * The Uri to check. * @return Whether the Uri authority is DownloadsProvider. */ private static boolean isDownloadsDocument(Uri uri) { return "com.android.providers.downloads.documents".equals(uri.getAuthority()); } /** * @param uri * The Uri to check. * @return Whether the Uri authority is MediaProvider. */ private static boolean isMediaDocument(Uri uri) { return "com.android.providers.media.documents".equals(uri.getAuthority()); } /** * 判断是否是Google相册的图片,类似于content://com.google.android.apps.photos.content/... **/ public static boolean isGooglePhotosUri(Uri uri) { return "com.google.android.apps.photos.content".equals(uri.getAuthority()); } /** * 判断是否是Google相册的图片,类似于content://com.google.android.apps.photos.contentprovider/0/1/mediakey:/local%3A821abd2f-9f8c-4931-bbe9-a975d1f5fabc/ORIGINAL/NONE/1075342619 **/ public static boolean isGooglePlayPhotosUri(Uri uri) { return "com.google.android.apps.photos.contentprovider".equals(uri.getAuthority()); }
2.2.3 针对Google相册图片获取方法
从相册中选择图片,如果手机安装了Google Photo,它的路径格式如下:
content://com.google.android.apps.photos.contentprovider/0/1/mediakey%3A%2Flocal%253A821abd2f-9f8c-4931-bbe9-a975d1f5fabc/ORIGINAL/NONE/1754758324
用原来的方式获取是不起作用的,path会是null,我们可以通过下面的形式获取:
/** * Google相册图片获取路径 **/ public static String getImageUrlWithAuthority(Context context, Uri uri) { InputStream is = null; if (uri.getAuthority() != null) { try { is = context.getContentResolver().openInputStream(uri); Bitmap bmp = BitmapFactory.decodeStream(is); return writeToTempImageAndGetPathUri(context, bmp).toString(); } catch (FileNotFoundException e) { e.printStackTrace(); }finally { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } return null; } /** * 将图片流读取出来保存到手机本地相册中 **/ public static Uri writeToTempImageAndGetPathUri(Context inContext, Bitmap inImage) { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); inImage.compress(Bitmap.CompressFormat.JPEG, 100, bytes); String path = MediaStore.Images.Media.insertImage(inContext.getContentResolver(), inImage, "Title", null); return Uri.parse(path); }
通过流的形式将图片读进来,转成bitmap形式,再写进手机媒体中,转换成路径如下:content://media/external/images/media/1754758324,因为Google中的图片并不在我们系统手机相册中,需要先下载,再转存。
3. 针对Android 7.0及以上机型打开Uri适配
Android7.0以上机型,若要在应用间共享文件,应发送一项 content:// URI,并授予 Uri临时访问权限。进行此授权的最简单方式是使用 FileProvider 类。如需了解有关权限和共享文件的详细信息,请参阅官方的共享文件。
具体需要在AndroidMainfest.xml文件中添加provider标签,并定义相应的路径,具体可参考鸿洋写的Android 7.0 行为变更 通过FileProvider在应用间共享文件吧
具体裁剪图片代码如下
/** * @param activity 当前activity * @param orgUri 剪裁原图的Uri * @param desUri 剪裁后的图片的Uri * @param aspectX X方向的比例 * @param aspectY Y方向的比例 * @param width 剪裁图片的宽度 * @param height 剪裁图片高度 * @param requestCode 剪裁图片的请求码 */ public static void cropImageUri(Activity activity, Uri orgUri, Uri desUri, int aspectX, int aspectY, int width, int height, int requestCode) { Intent intent = new Intent("com.android.camera.action.CROP"); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); orgUri = FileProvider.getUriForFile(activity, "com.smilexie.storytree.fileprovider", new File(newUri.getPath())); } intent.setDataAndType(orgUri, "image/*"); intent.putExtra("crop", "true"); intent.putExtra("outputX", width); intent.putExtra("outputY", height); intent.putExtra("scale", true); //将剪切的图片保存到目标Uri中 intent.putExtra(MediaStore.EXTRA_OUTPUT, desUri); intent.putExtra("return-data", false); intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString()); intent.putExtra("noFaceDetection", true); activity.startActivityForResult(intent, requestCode); }
只要是打开文件,uri都需要更改,不然解析异常,但是保存进uri则不需要修改,具体可参考:
Android 7.0 文件读取适配,适配相机及裁剪图片
总结
Android各版本,各机型适配不得不说有很多坑,比如我在开发中遇到google相册图片,老是获取不到路径。只能平时多关注Android新版本发布时,会对现有应用产生什么影响,遇到问题多查看别人的解决办法,多归纳,多总结,这样才能使咱们的应用越来越完善。
————————————————
版权声明:本文为CSDN博主「潇潇凤儿」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。