FileProvider使用
来源:互联网 发布:校园网络直播 编辑:程序博客网 时间:2024/05/18 02:31
目录
1.背景
2.问题
3.FileProvider
4.事例
5.原理
文章最后有代码链接
1.背景
targetSdkVersion:25
模拟器:genymotion api7.0
2.问题
Android7.0开始,应用私有目录被限制访问,官方做了如下限制:
1.私有文件的文件权限不应再由所有者放宽,使用MODE_WORLD_READABLE/MODE_WORLD_WRITEABLE将抛出异常
2.向应用外传递file://URI会出发FileUriExposedException
3.FileProvider
当targetSdkVersion>=24时,会存在上述问题,可能涉及到的场景有:拍照,程序安装等。
同时,官方在v4包(api=22开始)中引入FileProvider类用于程序间私有文件的共享。该类继承自ContentProvider,使用时需要在清单文件中注册。
1.使用方法
- 注册
在清单文件中通过标签注册,参考代码对属性进行说明:
注意:当自定义类继承自FileProvider时,需要更改name属性值为该类的相对路径…
<manifest> ... <application> ... <provider android:name="android.support.v4.content.FileProvider" android:authorities="com.mydomain.fileprovider" android:exported="false" android:grantUriPermissions="true"> ... </provider> ... </application></manifest>
- 设置可访问的共享文件
FileProvider只能对声明的文件夹下的文件生成uri,该文件夹的声明是在xml中使用标签完成的,下面的例子就是声明私有文件目录下images/下的文件可以临时访问(文件在res/xml/目录下),下面时一个简单的样式:
<paths xmlns:android="http://schemas.android.com/apk/res/android"> <files-path name="my_images" path="images/"/> ...</paths>
因为的子标签可以有多种,这里对所有进行说明:
子标签中属性说明:
实际使用时,一个path的定义格式应该如下:
<paths xmlns:android="http://schemas.android.com/apk/res/android"> <files-path name="my_images" path="images/"/> <files-path name="my_docs" path="docs/"/></paths>
- 完成配置
<provider android:name="android.support.v4.content.FileProvider" android:authorities="com.mydomain.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /></provider>
说明:
name:为固定值android.support.FILE_PROVIDER_PATHS
resource:所对应的xml文件路径
- 使用
1、通过路径生成要分享的文件File对象
2、使用FileProvider生成uri—FileProvider.getUriForFile()
3、客户端可以使用uri通过ContentResolver.openFileDescriptor获取到一个ParcelFileDescriptor
事例:
authrity为”com.mydomain.fileprovider”的FileProvider,获取到文件”default_image.jpg”文件,改文件位于”images”目录下
File imagePath = new File(Context.getFilesDir(), "images");File newFile = new File(imagePath, "default_image.jpg");Uri contentUri = getUriForFile(getContext(), "com.mydomain.fileprovider", newFile);
note:该代码生成的uri为 content://com.mydomain.fileprovider/my_images/default_image.jpg
- 临时权限的授予方式
1、使用Context.grantUriPermission(package,Uri,mode_flags)方法,使用想要的模式。这个方法通过mode_flags方法授予客户端package的临时权限,有两个取值,FLAG_GRANT_URI_PERMISSION和FLAG_GRANT_WRITE_URI_PERMISSOIN。该方式允许后,可通过revokeUriPermission终止,或者手机重启后
2、通过Intent- 通过Intent的setData()方法将该uri放入Intent中
- 可以为Intent设置flag,设置一个或两个, FLAG_GRANT_URI_PERMISSION和FLAG_GRANT_WRITE_URI_PERMISSOIN
- 将Intent发送给其他app,大部分情况,通过setResult()来做
这种方法获取的权限,当接收的Activity在栈中一直活跃时都会保留,当activity栈finish时,权限会自动移除。被允许的activity所在的app的其他组件也会被允许该权限。
4.事例
1.场景
将apk放在内部存储空间内,使用Intent进行安装
2.主要代码
- 将apk写入
public class App extends Application { public static final String TAG = "App"; private static String path; @Override public void onCreate() { super.onCreate(); writeApkToInner("app-debug.apk"); } public static String getPath() { return path; } private void writeApkToInner(String s) { File dir = getCacheDir(); if (!dir.exists()) dir.mkdir(); File[] apks = dir.listFiles(); for (File f : apks) { f.delete(); } File apk = new File(dir, "test.apk"); path = apk.getAbsolutePath(); InputStream is = null; OutputStream os = null; try { is = getResources().getAssets().open(s); os = new FileOutputStream(apk); byte[] buffer = new byte[1024]; int len = 0; while ((len = is.read(buffer)) > 0) { os.write(buffer, 0, len); os.flush(); } } catch (IOException e) { e.printStackTrace(); } finally { try { if (os != null) os.close(); if (is != null) is.close(); } catch (IOException e) { e.printStackTrace(); } } Log.e(TAG, "write into inner successful"); }}
- 安装代码
Intent intent = new Intent(Intent.ACTION_VIEW); Uri uriForFile = FileProvider.getUriForFile(this, "com.zzc.sample.fileprovider.fileprovider", new File(App.getPath())); Log.e(TAG, "uri" + uriForFile.toString()); intent.setDataAndType(uriForFile, "application/vnd.android.package-archive"); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); startActivity(intent);
- 清单文件
<application android:name=".App" android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> <provider android:name="android.support.v4.content.FileProvider" android:authorities="com.zzc.sample.fileprovider.fileprovider" android:grantUriPermissions="true" > <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/files_provider"> </meta-data> </provider> </application>
- path文件
<?xml version="1.0" encoding="utf-8"?><paths xmlns:android="http://schemas.android.com/apk/res/android"> <cache-path name="zzc_files" path="apks/"/></paths>
- uri对比
content://com.zzc.sample.fileprovider.fileprovider/zzc_files/test.apk
file:///data/user/0/com.zzc.sample.fileprovider/cache/apks/test.apk
5.原理
1.FileProvider内部
- PathStrategy
文件和uri之间的对应策略,不依赖动态,保证所有生成的uri能在进程被杀死并在之后的启动中保持一致。 - query方法
该方法会能获取文件名和文件大小的Cursor - openFile方法
该方法的主要作用是将文件的文件描述符FileDescriptor封装为ParcelFileDescriptor作为该方法的返回值,实现Parcelable接口,可在进程间传输
2.自定义处理
该模式依然采用ContentProvider-ContentResolver工作
1.客户端(文件被分享者)主要代码
- 清单文件
<activity android:name=".ResolverActivity"> <intent-filter> <action android:name="com.zzc.sample.client.SHARE"/> <data android:mimeType="zzc/client"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </activity>
- ResolverActivity
Intent intent = getIntent(); if (intent != null) { Uri data = intent.getData(); ContentResolver resolver = getContentResolver(); BufferedReader br = null; try { ParcelFileDescriptor parcelFileDescriptor = resolver.openFileDescriptor(data, "r"); FileDescriptor fd = parcelFileDescriptor.getFileDescriptor(); br = new BufferedReader(new FileReader(fd)); Log.e(TAG, "content---" + br.readLine()); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { if (br != null) { br.close(); } } catch (IOException e) { e.printStackTrace(); } } }
2.服务端(文件分享者)主要代码
- 写入Context.getCacheDir()/txts/hello.txt文件
private void writeFile(String s) { File dir = getCacheDir(); if (!dir.exists()) dir.mkdir(); dir = new File(dir, "txts"); if (!dir.exists()) dir.mkdir(); File file = new File(dir, s); filePath = file.getAbsolutePath(); if (!file.exists()) { OutputStreamWriter osw = null; try { osw = new OutputStreamWriter(new FileOutputStream(file)); osw.write("hello world"); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { if (osw != null) { osw.close(); } } catch (IOException e) { e.printStackTrace(); } } } }
- 授予临时权限
Uri data = FileProvider.getUriForFile(this, "com.zzc.sample.fileprovider.fileprovider", new File(App.getFilePath())); Intent intent = new Intent("com.zzc.sample.client.SHARE"); intent.setDataAndType(data, "zzc/client"); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); startActivity(intent);
3.输出 content---hello world
如果文章有不足之处,欢迎指出
官网链接
代码下载
- FileProvider使用
- FileProvider使用
- 使用FileProvider
- 使用FileProvider共享文件
- FileProvider控件的使用
- FileProvider的使用
- 7.0fileprovider的使用
- 使用FileProvider共享文件
- Android FileProvider的使用
- FileProvider
- FileProvider
- FileProvider
- Android 7.0 FileProvider的使用
- Android 7.0 FileProvider的使用
- Android 7.0 FileProvider的使用
- Android7.0使用FileProvider安装apk
- android 拍照使用fileprovider遇到的坑
- Android7.0 使用FileProvider 共享文件
- java生成Zip文件,无法删除,资源被占用问题
- yum 安装问题
- JavaScript流程控制-循环结构
- 2440移植Linux Kernel笔记(五)------制作yaffs2映像文件
- eclipse导入war包二次开发
- FileProvider使用
- Linux下安装和配置Redis 3.2.8
- 基于NDK编译ffmpeg库(三)
- 【Java8源码分析】线程-Thread类的全面剖析
- 从 JavaScript 作用域说开去
- 糗事百科段子爬取
- Activity与Fragment易混点归纳
- css笔记(三)属性
- PAT 1004. 成绩排名