Android学习之搞搞7.0和6.0uri的不同
来源:互联网 发布:下颌第一磨牙雕刻数据 编辑:程序博客网 时间:2024/06/05 01:07
最近做项目的时候,遇到一个问题,调用系统相册,进行裁剪,在系统7.0的手机上会得不到这个图片,因为说文件不可以获取,在7.0以下就可以
当时负责这个模块的同学是按照一行代码敲的,但是有问题,所以我想研究研究,这是为什么
我先写了一段代码:
是这样的:
private void openFile(){ Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("image/*"); startActivityForResult(intent,1,null); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if(requestCode == 1){ if(resultCode == RESULT_OK){ Uri uri = data.getData(); Log.d("pathPart",uri.toString()); Log.d("pathPart",uri.getPath()); Log.d("pathPart",uri.getAuthority()); Log.d("pathPart",uri.getHost()); Log.d("pathPart",uri.getScheme()); } } super.onActivityResult(requestCode, resultCode, data); }
首先,我想看看系统6.0得到的uri是什么样子的:
D/pathPart: content://com.android.providers.media.documents/document/image%3A712757
D/pathPart: /document/image:712757
D/pathPart: com.android.providers.media.documents
D/pathPart: com.android.providers.media.documents
D/pathPart: content
这里的getPath就是对应的图片在数据库中的存在。
所以接下来我们要去得到这张图片,就需要通过ContentProvider进行数据库的搜索
但是手机上关于图片的类型有很多,我刚刚测试一张我从qq接收的图片,它的uri是关于这样的
D/pathPart: content://media/external/images/media/712719
D/pathPart: /external/images/media/712719
D/pathPart: media
D/pathPart: media
D/pathPart: content
所以在第一行代码中,有一段关于在api19上的代码:
首先,我们判断是不是document,因为document的类型的uri是不一样的
content://com.android.providers.media.documents/document/image%3A712757
然后我们再判断是不是媒体文件,还是下载的文件,因为所对应的在系统中的uri还一样
如果是媒体文件,我们先得到id,这个是图片在数据库中的_ID的值
我在思考为什么要将图片的authority进行改变
我打印一下这个:
Log.d(“mediaPath”, MediaStore.Images.Media.EXTERNAL_CONTENT_URI.toString());
结果是:
content://media/external/images/media
发现它和图片的uri的authority一样,这是为什么呢?
然后查看源码解释,有一个这样的:
Allow the user to select a particular kind of data and
return it. This is different than {@link #ACTION_PICK} in that here we
just say what kind of data is desired, not a URI of existing data from
which the user can pick.
也就是说通过ACTION_CONTENT返回的uri只是用来说明是那一种类型的数据,而不是现有数据真正的uri。它只有图片的编号
因此我们需要通过系统真正的uri来得到真正的uri,所以有了上面一段代码
再查看mediaStore的源码,关于它的介绍是这样的:
/** * The Media provider contains meta data for all available media on both internal * and external storage devices. */public final class MediaStore { private final static String TAG = "MediaStore"; public static final String AUTHORITY = "media"; private static final String CONTENT_AUTHORITY_SLASH = "content://" + AUTHORITY + "/";
我们可以看到,它才是安卓给我们封装的真正的媒体提供器,它包含了所有可用媒体在内存储存设备上的数据
因此我们要进行解析!
再通过ContentResolve进行查询,得到真正的文件地址
然后我在思考这个DocumentsContract
是啥,依旧打开源码
/** * Defines the contract between a documents provider and the platform. * <p> * To create a document provider, extend {@link DocumentsProvider}, which * provides a foundational implementation of this contract. * <p> * All client apps must hold a valid URI permission grant to access documents, * typically issued when a user makes a selection through * {@link Intent#ACTION_OPEN_DOCUMENT}, {@link Intent#ACTION_CREATE_DOCUMENT}, * {@link Intent#ACTION_OPEN_DOCUMENT_TREE}, or * {@link StorageVolume#createAccessIntent(String) StorageVolume.createAccessIntent}. * * @see DocumentsProvider */public final class DocumentsContract { private static final String TAG = "DocumentsContract";
它是用来定义文档提供器和平台之间的联系的
然后我们看这个方法的实现:
/** * Test if the given URI represents a {@link Document} backed by a * {@link DocumentsProvider}. * * @see #buildDocumentUri(String, String) * @see #buildDocumentUriUsingTree(Uri, String) */ public static boolean isDocumentUri(Context context, @Nullable Uri uri) { if (isContentUri(uri) && isDocumentsProvider(context, uri.getAuthority())) { final List<String> paths = uri.getPathSegments(); if (paths.size() == 2) { return PATH_DOCUMENT.equals(paths.get(0)); } else if (paths.size() == 4) { return PATH_TREE.equals(paths.get(0)) && PATH_DOCUMENT.equals(paths.get(2)); } } return false; }
继续深入,我找到documentProvider的源码
Base class for a document provider. A document provider offers read and write access to durable files, such as files stored on a local disk, or files in a cloud storage service.
这是对它的介绍
大概说文档提供器访问和写入持久文件,像那些存储在本地磁盘的文件,或者在云服务器上的文件
然后我们再看看真实地址:
private String getPath(Uri uri,String selection){ String path = ""; ContentResolver resolver = getContentResolver(); Cursor cursor = resolver.query(uri,null,selection,null,null); if(cursor!=null){ if(cursor.moveToFirst()){ path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA)); } } cursor.close(); Log.d("filePath",path); return path; }
通过这段代码:
我得到一张图片的真实地址:
D/filePath: /storage/emulated/0/DCIM/Camera/IMG_20171113_143115.jpg
然后用imageview展示出来
这是我用的我的手机,6.0系统测的
现在我们换成7.0的
如果只是展示图片,没有什么问题:
D/pathPart:content://com.android.providers.media.documents/document/image%3A65
D/pathPart: /document/image:65
D/pathPart: com.android.providers.media.documents
D/pathPart: com.android.providers.media.documents
D/pathPart: content
D/mediaPath: content://media/external/images/medie
D/filePath:/storage/emulated/0/DCIM/Camera/IMG_20171113_112835.jpg
现在我对图片进行裁剪:
我先使用这段代码:
public void startPhotoZoom(Uri inputUri) { if (inputUri == null) { Log.d("eee","The uri is not exist."); return; } Intent intent = new Intent("com.android.camera.action.CROP"); //sdk>=24 mCropFile = new File(getFilesDir().getAbsoluteFile()+"//crop.jpg"); Uri outPutUri = Uri.fromFile(mCropFile); intent.setDataAndType(inputUri, "image/*"); intent.putExtra(MediaStore.EXTRA_OUTPUT, outPutUri); intent.putExtra("noFaceDetection", false);//去除默认的人脸识别,否则和剪裁匡重叠 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); // 设置裁剪 intent.putExtra("crop", "true"); // aspectX aspectY 是宽高的比例 intent.putExtra("aspectX", 1); intent.putExtra("aspectY", 1); // outputX outputY 是裁剪图片宽高 intent.putExtra("outputX", 200); intent.putExtra("outputY", 200); intent.putExtra("return-data", false); intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());// 图片格式 startActivityForResult(intent, 3);//这里就将裁剪后的图片的Uri返回了 }
在我启动裁剪功能的时候,出现:
就是这个坑!!!!!!
那是7.0又加了一些东西
我们不能直接用
我在给裁剪代码传递uri的的时候,是这样传的:
Uri uri1 = Uri.fromFile(new File(imagePath)); //原谅用这个,不知道为啥,看不到log System.out.println(uri1.toString()); startPhotoZoom(uri1);
这个输出出来是:
file:///storage/emulated/0/Tencent/QQ_Images/1510631227113.jpeg
然后就报错啦,不允许这样用!!!!
怎么解决呢,要将其转成content开头的uri
这个时候就要用到FileProvider了
首先在Manifest中注册provider
<provider android:authorities="com.vivian.provider" android:name="android.support.v4.content.FileProvider" android:exported="false" //不允许外界访问 android:grantUriPermissions="true" > <meta-data android:name="android.support.FILE_PROVIDER_PATHS"//固定值 android:resource="@xml/filepath"/> </provider>
然后在xml文件夹下定义:
<paths> <!-- xml文件是唯一设置分享的目录 ,不能用代码设置 1.<files-path> getFilesDir() /data/data//files目录 2.<cache-path> getCacheDir() /data/data//cache目录 3.<external-path> Environment.getExternalStorageDirectory() SDCard/Android/data/你的应用的包名/files/ 目录 4.<external-files-path> Context#getExternalFilesDir(String) Context.getExternalFilesDir(null). 5.<external-cache-path> Context.getExternalCacheDir(). --> <!-- path :代表设置的目录下一级目录 eg:<external-path path="images/" 整个目录为Environment.getExternalStorageDirectory()+"/images/" name: 代表定义在Content中的字段 eg:name = "myimages" ,并且请求的内容的文件名为default_image.jpg 则 返回一个URI content://com.example.myapp.fileprovider/myimages/default_image.jpg --> <!--当path 为空时 5个全配置就可以解决--> <!--下载apk--> <external-path path="" name="sdcard_files" /> <!--相机相册裁剪--> <external-files-path path="" name="camera_has_sdcard"/> <files-path path="" name="camera_no_sdcard"/></paths>
然后更改上面的代码,这里我只是测试7.0的情况
Uri uri1 = Uri.fromFile(new File(imagePath)); Uri inputuri = FileProvider.getUriForFile(MainActivity.this,"com.vivian.provider",new File(imagePath)); System.out.println(uri1.toString()); System.out.println(inputuri.toString()); startPhotoZoom(inputuri);
然后设置图片:
Bundle bundle = data.getExtras(); if(bundle!=null){ Bitmap bitmap = bundle.getParcelable("data"); mImageView.setImageBitmap(bitmap); }
我们看看打印的东西:
D/filePath:/storage/emulated/0/DCIM/Camera/IMG_20171113_112733.jpg
System.out:file:///storage/emulated/0/DCIM/Camera/IMG_20171113_112733.jpg
System.out:content://com.vivian.provider/sdcard_files/DCIM/Camera/IMG_20171113_112733.jpg
这个是7.0得到的文件的uri和 转成content uri
public void startPhotoZoom(Uri inputUri) { if (inputUri == null) { Log.d("eee","The uri is not exist."); return; } Intent intent = new Intent("com.android.camera.action.CROP"); //sdk>=24 mCropFile = new File(getFilesDir().getAbsoluteFile()+"//crop.jpg"); Uri outPutUri = Uri.fromFile(mCropFile); intent.setDataAndType(inputUri, "image/*"); intent.putExtra(MediaStore.EXTRA_OUTPUT, outPutUri); intent.putExtra("noFaceDetection", false);//去除默认的人脸识别,否则和剪裁匡重叠 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); // 设置裁剪 intent.putExtra("crop", "true"); // aspectX aspectY 是宽高的比例 intent.putExtra("aspectX", 1); intent.putExtra("aspectY", 1); // outputX outputY 是裁剪图片宽高 intent.putExtra("outputX", 200); intent.putExtra("outputY", 200); intent.putExtra("return-data", true); //一定要是true才能返回数据 intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());// 图片格式 startActivityForResult(intent, 3);//这里就将裁剪后的图片的Uri返回了// }catch (IOException e){// e.printStackTrace();// } }
参考:
http://www.jianshu.com/p/ba57444a7e69
项目地址:
有点乱。。。。。
https://github.com/vivianluomin
- Android学习之搞搞7.0和6.0uri的不同
- android的URI学习
- Android的URI学习
- Android学习_ContentProvider和Uri
- 关于escape和URI之间的不同!
- Android面试之URI和URL的区别
- Android开发之Uri、UriMatcher、ContentUris学习
- Android之ContentProvider(二):Uri对象的内容URI
- android之ContentProvider和Uri详解
- android之ContentProvider和Uri详解
- android之ContentProvider和Uri详解
- android之ContentProvider和Uri详解
- 搞搞吧的模式方式值得我们学习
- Android使用URI启动应用的学习
- android之Uri的常用几个例子
- android之Uri的常用几个例子
- android之Uri的常用几个例子
- android之Uri的常用几个例子
- 剑指offer——面试题35:第一个只出现一次的字符
- 一些常见的算法,包括选择排序法,冒泡排序法,折半查找法,和函数的使用;
- 信息学奥赛一本通(C++版) 第二部分 基础算法 第八章 广度优先搜索算
- Web AppBuilder学习笔记1
- nginx 常用命令
- Android学习之搞搞7.0和6.0uri的不同
- 欢迎使用CSDN-markdown编辑器
- 四元数与复数之间的关系
- 回文判断
- Matlab实现 理想低通、巴特沃斯低通、高斯低通、理想高通、巴特沃斯高通、高斯高通(d=10,50,150)
- 深度递归学习
- Linux 命令
- 1.笔记 5.7.10MySQL——安装
- leetcode题解-38. Count and Say