FileProvider文件分享

文件共享

​ file:// 协议不再支持共享,需要用content:// 协议。

创建FileProvider

<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="app的包名.fileProvider"
    android:grantUriPermissions="true"
    android:exported="false">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths" />
</provider>

创建xml

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <!--内置SD卡 Environment.getExternalStorageDirectory() .表示共享所有的目录,也可以指定共享的目录-->
    <external-path
        name="external-path"
        path="."/>
    <!--内置SD卡 Context.getExternalCacheDir() .表示共享所有的目录,也可以指定共享的目录-->
    <external-cache-path
        name="external-cache-path"
        path="."/>
    <!--内置SD卡 Context.getExternalFilesDir(null) .表示共享所有的目录,也可以指定共享的目录-->
    <external-files-path
        name="external-files-path"
        path="."/>
    <!--data目录下 Context.getFilesDir() .表示共享所有的目录,也可以指定共享的目录-->
    <files-path
        name="files_path"
        path="."/>
    <!--data缓存目录 Context.getCacheDir() .表示共享所有的目录,也可以指定共享的目录-->
    <cache-path
        name="cache-path"
        path="."/>
    <!--这个标签Android官方文档中是没有提及,Android设备的根目录,该目录下包含着手机内部存储器,外置SD卡等所有文件的目录-->
    <root-path
        name="name"
        path="."/>
</paths>

使用

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    Uri uri = FileProvider.getUriForFile(CameraActivity.this, "app的包名.fileProvider", photoFile);
} else {
    Uri uri = Uri.fromFile(photoFile);
}

源码解析

  • 核心方法:

    public static Uri getUriForFile(@NonNull Context context, @NonNull String authority,
                @NonNull File file) {
            final PathStrategy strategy = getPathStrategy(context, authority);
            return strategy.getUriForFile(file);
        }
    
  • 核心中间接口PathStrategy,file和uri映射

    interface PathStrategy {
        Uri getUriForFile(File file);
        File getFileForUri(Uri uri);
    }
    
  • 缓存pathStrategy,下次就不需要解析了

    private static HashMap<String, PathStrategy> sCache = new HashMap<String, PathStrategy>();
     PathStrategy strat;
            synchronized (sCache) {
                strat = sCache.get(authority);
                if (strat == null) {
                    try {
                        strat = parsePathStrategy(context, authority);
                    } catch (IOException e) {
                   。。。
                    sCache.put(authority, strat);
                }
            }
    
  • 解析PathStrategy,返回SimplePathStrategy

    这里的核心类就是XmlResourceParser,用来解析xml

    final SimplePathStrategy strat = new SimplePathStrategy(authority);
            final ProviderInfo info = context.getPackageManager()
                    .resolveContentProvider(authority, PackageManager.GET_META_DATA);
            if (info == null) {
            // 这里就是配置meta的检验
                throw new IllegalArgumentException(
                        "Couldn't find meta-data for provider with authority " + authority);
            }
    				// 解析我们写的那个file_path.xml
    				// 其中META_DATA_FILE_PROVIDER_PATHS = "android.support.FILE_PROVIDER_PATHS";这个字符串是要卸载provider的meta中的
            final XmlResourceParser in = info.loadXmlMetaData(
                    context.getPackageManager(), META_DATA_FILE_PROVIDER_PATHS);
            if (in == null) {
                throw new IllegalArgumentException(
                        "Missing " + META_DATA_FILE_PROVIDER_PATHS + " meta-data");
            }
    
            int type;
            while ((type = in.next()) != END_DOCUMENT) {
                if (type == START_TAG) {
                // tag、name、path都是我们配置在xml里的
                    final String tag = in.getName();
    
                    final String name = in.getAttributeValue(null, ATTR_NAME);
                    String path = in.getAttributeValue(null, ATTR_PATH);
    
                    File target = null;
                    // tag 类型如下:
                    private static final String TAG_ROOT_PATH = "root-path";
                    private static final String TAG_FILES_PATH = "files-path";
                    private static final String TAG_CACHE_PATH = "cache-path";
                    private static final String TAG_EXTERNAL = "external-path";
                    private static final String TAG_EXTERNAL_FILES = "external-files-path";
                    private static final String TAG_EXTERNAL_CACHE = "external-cache-path";
                    private static final String TAG_EXTERNAL_MEDIA = "external-media-path";
                    
                    if (TAG_ROOT_PATH.equals(tag)) {
                        target = DEVICE_ROOT;
                    } else if (TAG_FILES_PATH.equals(tag)) {
                        target = context.getFilesDir();
                    } else if (TAG_CACHE_PATH.equals(tag)) {
                        target = context.getCacheDir();
                    } else if (TAG_EXTERNAL.equals(tag)) {
                        target = Environment.getExternalStorageDirectory();
                    } else if (TAG_EXTERNAL_FILES.equals(tag)) {
                        File[] externalFilesDirs = ContextCompat.getExternalFilesDirs(context, null);
                        if (externalFilesDirs.length > 0) {
                            target = externalFilesDirs[0];
                        }
                    } else if (TAG_EXTERNAL_CACHE.equals(tag)) {
                        File[] externalCacheDirs = ContextCompat.getExternalCacheDirs(context);
                        if (externalCacheDirs.length > 0) {
                            target = externalCacheDirs[0];
                        }
                    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
                            && TAG_EXTERNAL_MEDIA.equals(tag)) {
                        File[] externalMediaDirs = context.getExternalMediaDirs();
                        if (externalMediaDirs.length > 0) {
                            target = externalMediaDirs[0];
                        }
                    }
    
                    if (target != null) {
                    // buildPath(target, path)就是根据配置重新生成path
                        strat.addRoot(name, buildPath(target, path));
                    }
                }
            }
    
  • SimplePathStrategy实现想要的content://

    • 添加到集合里

      				private final HashMap<String, File> mRoots = new HashMap<String, File>();
              /**
               * Add a mapping from a name to a filesystem root. The provider only offers
               * access to files that live under configured roots.
               */
              void addRoot(String name, File root) {
                  if (TextUtils.isEmpty(name)) {
                      throw new IllegalArgumentException("Name must not be empty");
                  }
                  try {
                      // Resolve to canonical path to keep path checking fast
                      root = root.getCanonicalFile();
                  } catch (IOException e) {
                      throw new IllegalArgumentException(
                              "Failed to resolve canonical path for " + root, e);
                  }
                  mRoots.put(name, root);
              }
      
    • 从roots遍历并解析到path,修改协议,华丽转身

      new Uri.Builder().scheme("content").authority(mAuthority).encodedPath(path).build();
      
  • 回到我们出发的地方

    public static Uri getUriForFile(@NonNull Context context, @NonNull String authority,
                @NonNull File file) {
            final PathStrategy strategy = getPathStrategy(context, authority);
            // 这里的strategy的实例是SimplePathStrategy
            return strategy.getUriForFile(file);
        }
    

android 11 uri 分享权限

在这里插入图片描述

感悟

  • 两层缓存:sCache缓存了PathStrategy,SimplePathStrategy里的mRoots缓存了File
  • 接口定义,子类实现PathStrategy–>SimplePathStrategy

微信分享

  • 如果用的是File的形式分享,则需要用到FileProvider,如果是bitmap直接分享,是用不到的。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值