AndroidN
1.File文件的路径转化为Uri方式
文件:File file=new File(MainActivity.this.getExternalFilesDir(Environment.DIRECTORY_PICTURES),"/zwk/" + “zwk”+ “.jpg”);
路径是:/storage/emulated/0/Android/data/com.example.androidndemo/files/Pictures/zwk/zwk.jpg
-
4.4之前
Uri imageUri = Uri.fromFile(file);
转化后的Uri:file:///storage/emulated/0/Android/data/com.example.androidndemo/files/Pictures/zwk/zwk.jpg -
7.0之后
Uri imageUri = FileProvider.getUriForFile(MainActivity.this, MainActivity.this.getPackageName() + ".fileprovider", file);
转化后Uri:content://com.example.androidndemo.fileprovider/external-files-path/Pictures/zwk/zwk.jpg
**差别:**7.0前后的差别
- ①是file://变成了content://.
- ②多了com.example.androidndemo.fileprovider
- ③/storage/emulated/0/Android/data/com.example.androidndemo/files/变成了/external-files-path/
关于这点差别,我们有必要看下androidN的知识。
2.AndroidN知识点
做过拍照功能,肯定遇到过这个异常:
android.os.FileUriExposedException: file:///xxxxx exposed beyond app through ClipData.Item.getUri()
这就是7.0的改变导致的版本的不适配问题。
-
7.0为啥改变啥?
因为fill://这种URI可能会留下无法访问的路径问题,触发FileUriExposedException异常。所以,7后分享私有文件内容推荐使用FileProvider这种方法。 -
7.0适配
- 拍照适配
-
7.0前拍照
使用6.0动态权限,先获取读写权限,然后进行调用拍照保存到指定路径:/storage/emulated/0/Android/data/com.example.androidndemo/files/Pictures/zwk/zwk.jpg
-
File file = new File(MainActivity.this.getExternalFilesDir(Environment.DIRECTORY_PICTURES), "/zwk/" + "zwk" + ".jpg"); if (!file.getParentFile().exists()) file.getParentFile().mkdirs(); Uri imageUri = Uri.fromFile(file); Intent intent = new Intent(); intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);//设置Action为拍照 intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);//将拍取的照片保存到指定URI startActivityForResult(intent, 10086);
-
7.0拍照
①使用6.0动态权限,先获取读写权限。
②新建一个名为files-path.xml的文件
<paths xmlns:android="http://schemas.android.com/apk/res/android"> <files-path name="files-path" path="." /> <cache-path name="cache-path" path="." /> <external-path name="external-path" path="." /> <external-files-path name="external-files-path" path="." /> <external-cache-path name="external-cache-path" path="." /> <external-media-path name="external-media-path" path="." /> <root-path name="root" path=" " /> </paths>
-
paths:是路径别名的集合,其内必须有一个files-path
-
files-path:相当于context.getFilesDir().getAbsolutePath()获取的路径/data/user/0/com.example.androidndemo/files
-
cache-path:相当于context.getCacheDir().getAbsolutePath()获取的路径/data/user/0/com.example.androidndemo/cache
-
exteral-path:相当于Environment.getExternalStorageDirectory().getAbsolutePath()获取的路径/storage/emulated/0
-
external-files-path:相当于Context.getExternalFilesDir(null)获取的路径/storage/emulated/0/Android/data/com.example.androidndemo/files
-
external-cache-path:相当于context.getExternalCacheDir().getAbsolutePath()获取的路径/storage/emulated/0/Android/data/com.example.androidndemo/cache
-
path:path=".“代表目录整个目录 而 path=”/MyImage"代表一个子目录.
如<files-path name="files-path" path="." />
代表了/data/user/0/com.example.androidndemo/files
<files-path name="files-path" path="/MyImage" />
代表了/data/user/0/com.example.androidndemo/files/MyImage 这个目录 -
name:相当于一个路径的别名,代表了一段路径。具体和父标签和path值有关,如上面的例子.
- 拍照适配
③ manifest的application下注明
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.example.androidndemo.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_path" />
- name="android.support.v4.content.FileProvider"或者androidx.core.content.FileProvider
- authorities:一个标识常量,要确保唯一并且和④中FileProvider.getUriForFile()第二个参数保持一致,一般使用包名+.fileprovider.
- exported:限制不让外部程序调用.
- grantUriPermissions:设置为true,允许获得文件临时的访问权限
- name:android.support.FILE_PROVIDER_PATHS 这个为啥这么写,没搞清楚可能是固定的写法
- resource:可以共享的文件的路径
④ 拍照获取Uri的时候,将代码替换成
Uri imageUri = FileProvider.getUriForFile(MainActivity.this, MainActivity.this.getPackageName() + ".fileprovider", file);
File file = new File(MainActivity.this.getExternalFilesDir(Environment.DIRECTORY_PICTURES), "/zwk/" + "zwk" + ".jpg");
if (!file.getParentFile().exists()) file.getParentFile().mkdirs();
imageUri = FileProvider.getUriForFile(MainActivity.this, MainActivity.this.getPackageName() + ".fileprovider", file);
Intent intent = new Intent();
intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);//设置Action为拍照
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);//将拍取的照片保存到指定URI
startActivityForResult(intent, 10086);
-
下载安装app
Intent installIntent = new Intent(Intent.ACTION_VIEW); installIntent.addFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED | Intent.FLAG_ACTIVITY_NEW_TASK); // 7.0 uri权限 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { installIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); Uri uri = FileProvider.getUriForFile(Global.context, getApplicationContext().getPackageName() + ".provider", new File(updateDir, fileName)); installIntent.setDataAndType(uri, "application/vnd.android.package-archive"); // 兼容8.0 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { boolean hasInstallPermission = getPackageManager().canRequestPackageInstalls(); if (hasInstallPermission) { Log.d("zwk", "有安装未知应用的安装权限"); installIntent.setDataAndType(Uri.fromFile(new File(updateDir, fileName)), "application/vnd.android.package-archive"); } else { Log.d("zwk", "没有安装未知应用的安装权限"); startInstallPermissionSettingActivity(); return; } } } else { installIntent.setDataAndType(Uri.fromFile(new File(updateDir, fileName)), "application/vnd.android.package-archive"); } startActivity(installIntent);
7.0前和后的区别,还是Uri变更导致的,只需要进行版本判断采用相应的方法就可以适配.
-
分享
使用Intent进行应用A和应用B之间得分享得时候,如果在7.0上应用B显示图片得时候常常会出现如下异常:
Permission Denial: opening provider android.support.v4.content.FileProvider from ProcessRecord{f7cd2c 3445:com.example.androidnseconddemo/u0a88} (pid=3445, uid=10088) that is not exported from uid 10085
at android.os.Parcel.readException(Parcel.java:1684)
原因就是7.0中分享,应用A需要加入临时权限
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
具体代码:
应用A:
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.setDataAndType(imageUri, "image/*");
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);// 权限
startActivity(shareIntent);
应用B:
Intent intent = getIntent();
imgUri = intent.getData();
imageView.setImageBitmap(BitmapFactory.decodeStream(getContentResolver().openInputStream(imgUri)));
-
Uri 适配
Uri imageUri; if (Build.VERSION.SDK_INT>Build.VERSION_CODES.N){ imgUri=FileProvider.getUriForFile(context,authority,file); }else{ imageUri = Uri.fromFile(file) }