FileProvider 是在 v4 support library 里的, 使用之前要添加 v4 support library

默认 v7 support 继承了 v4

  1. manifest中指定分享的文件, 具体的分享目录路径在res/xml/filepaths中指定, android:authorities为指定的认证, 自行定义, 一般为 package name + fileprovider, 其他为固定写法
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myapp">
	  <application
        ...>
        
        <provider
        	android:name="android.support.v4.content.FileProvider"
            android:authorities="com.example.myapp.fileprovider"
            android:grantUriPermissions="true"
            android:exported="false">
        	<meta-data
            	android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/filepaths"/>
        </provider>
        
        ...
    </application>
</manifest>
  1. res/xml/filepaths写法

    1
    2
    3
    4
    5
    6
    7
    
    <paths>
      <files-path path="images" name="my-images"/>
      <cache-path path="..." name="..."/>
      <external-files-path path="...." name="..."/>
      <external-cache-path path="...." name="..."/>
      <external-path path="..." name="...."/>
    </paths>
    
    • file-pathContext.getFilesDir()代表的目录, 内部(internal)存储目录files/.可能有两个位置, /data/data/package_name/files下, 或者/data/user/../package_name/files
    • cache-pathContext.getCacheDir()代表的内部目录 cache/
    • external-files-path为外部存储目录Context#getExternalFilesDir(String)Context.getExternalFilesDir(null)这个目录一般在外部存储中的Android/data/package_name/files
    • external-cache-path为外部缓存目录Android/data/package_name/cache中, 由Context.getExternalCacheDir()获得
    • external-path为外部存储的根目录Context.getExternalStorageDirectory()

    如果使用外部目录, 因为存在 SDCard可插拔的原因, 最好使用Environment.getExternalStorageState()来检测当前外部存储状态, 返回Environment.MEDIA_MOUNTED表示存储可用.

path="path"

代表是指定目录下的子目录, 或者当前目录(使用.表示共享当前目录)

name="name"

代表将共享的目录名称(path)在生成content://uri时替换成这个值

  1. 使用FileProvider.getUriForFile(Context, String, File)方法来获取共享的文件uri路径

    1
    2
    3
    4
    5
    
    /* 生成 content://com.example.myapp.fileprovider/my_images/default_image.jpg */
    File imagePath = new File(context.getFilesPath(), "images");
    File newFile = new File(imagePath, "default_image.jpg");
    Uri contentUri = FileProvider.getUriForFile(context, "com.example.myapp.fileprovider",
         newFile);
    

    authorities参数要和meta-data中写的一样, 不然会找不到共享目录, 抛出错误. getUriForFile会在设定好的xml文件中从上逐行进行匹配, 若匹配不到任何一行, 抛出IllegalArgumentException

  2. uri授予权限, 为了兼容Kitkat(SDK 19)及以下的Android版本, 一定要对uri授予读写权限. 在Kitkat以上的版本会自动授予权限.

    • 使用Context.grantUriPermissioin(package, Uri, mode_flags)来授权, 比如Intent.FLAG_GRANT_READ_URI_PERMISSIOINIntent.FLAG_GRANT_WRITE_URI_PERMISSION标志位, 在适当的时候使用Context.revokeUriPermission(Uri, mode_flags)来去掉授予的权限
    • 或者使用Intent.setData()uri放到intent中后, 使用Intent.setFlags()来设置权限标志位, 建议使用这种授权方式, 这样就不用手动来取消授权了
    1
    2
    3
    4
    5
    6
    7
    
    List<ResolveInfo> resolveActivities = getPackageManager().queryInetntActivities(
         imageCapture, PackageManager.MATCH_DEFAULT_ONLY);
    /* 为每个activity授权 */
    for (ResolveInfo resolveActivity : resolveActivities) {
        grantUriPermission(resolveActivity.activityInfo.packageName, uri,
             Intent.FLAG_GRANT_WRITE_PERMISSION);
    }
    

Reference:
  1. [Android Developer FileProvider](https://developer.android.google.cn/reference/android/support/v4/content/FileProvider.html?hl=zh-cn#getUriForFile(android.content.Context, java.lang.String, java.io.File))
  2. Android Tranning Share a File