21

我已经按照这个 Google 教程开始了使用new Intent(MediaStore.ACTION_IMAGE_CAPTURE). 本教程建议使用getExternalStoragePublicDirectory对我的应用非常有用的公共目录。但随后他们的示例改为使用getExternalFilesDir. 然后要将文件的 URI 传递给意图,MediaStore.EXTRA_OUTPUT必须获取一个内容 URI,因为我想以 Android N 为目标。在定位 NI 之前,只需传递一个 file:// URI,除了 Google 之外的所有人都很高兴。现在对 NI 开始弄了FileUriExposedException,好像不是很优惠

所以鉴于我有这样的文件......

private File createImageFile() throws IOException {
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
    File storageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "MyAppFolder");
    if (!storageDir.exists() && !storageDir.mkdir())
        Log.w(TAG, "Couldn't create photo folder: " + storageDir.getAbsolutePath());
    File image = new File(storageDir, timeStamp + ".jpg");
    mCurrentPhotoPath = image.getAbsolutePath();
    return image;
}

...我可以使用公共图片目录的内置提供程序来获取内容 URI 吗?如果有怎么办?

我试过类似的东西

takePictureIntent.putExtra(
    MediaStore.EXTRA_OUTPUT, 
    FileProvider.getUriForFile(this, MediaStore.AUTHORITY, createImageFile()));

但它只是抛出

IllegalArgumentException:缺少 android.support.FILE_PROVIDER_PATHS 元数据

那个权威是正确的吗?如果我必须使用自己的提供程序来共享公共文件,那么我可以在 FILE_PROVIDER_PATHS 元数据中指定什么路径?我能找到的所有选项都是针对私有目录的。

4

2 回答 2

16

TL:DR<external-path name="MyAppFolder" path="." />有效,但它安全吗?

您将需要制作自己的FileProvider. 值得阅读它的文档,但这里有一个简短的概要。

正如@CommonsWare 所提到的,您需要MediaStore.AUTHORITY从您的示例中替换为"com.example.fileprovider",这是您将在清单中定义的权限,如下所示......

<application>AndroidManifest 的标记中,键入以下内容:

<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="com.example.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths" />
</provider>

您得到的例外是由于没有链接到包含您可以触摸<meta-data>的所有路径的 xml 文件的标记。FileProvider但这正是我一直在为自己苦苦挣扎的文件中的内容。

在您链接的教程即将结束时,在保存全尺寸照片部分的末尾,谷歌为此提供的示例file_paths.xml是:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="my_images" path="Android/data/com.example.package.name/files/Pictures" />
</paths>

"my_images""MyAppFolder"在您的示例中将替换为。在那个教程中谷歌声称

路径组件对应于使用 Environment.DIRECTORY_PICTURES 调用时由 getExternalFilesDir() 返回的路径。确保将 com.example.package.name 替换为应用的实际包名称。

...并输入它让我意识到这不是指您和我正在寻找的公共图片目录。这解释了为什么使用该路径会产生此异常:

java.lang.IllegalArgumentException:找不到包含 /storage/emulated/0/Pictures/MyAppFolder/{filename}.jpg 的已配置根目录

我将把它留在这里以供将来参考,然后继续回答你的大问题——你可以指定什么路径来允许其他应用程序与你的应用程序创建的公共图片目录中的文件进行交互,目标 API 级别为 24+?

正如@CommonsWare 所说,“所需子目录的详细信息因设备而异”,因此您无法声明公共图片目录的路径,但我确实找到了一种可行的方法。

<external-path name="MyAppFolder" path="." />将允许您FileProvider向其他应用程序(如相机)提供内容 URI,然后该应用程序可以读取和写入它解析的文件。

如果这有任何危险或不利之处,我很想听听。

于 2016-12-21T02:03:49.470 回答
7

这是我在我的应用程序中使用的图片公共目录。

  1. 像这样创建文件:

    File theFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "YOUR_APP_NAME" + File.separator + "YOUR_FILE_NAME");
    
  2. 在文件提供者的元数据中指定以下路径:

    <paths>
        <external-path name="secure_name" path="Pictures/YOUR_APP_NAME" />
    </paths>
    

“secure_name”用于隐藏您出于安全原因共享的子目录的名称(在本例中为“YOUR_APP_NAME” )。

“图片”对应于Environment.DIRECTORY_PICTURES

  1. 获取Uri以发送Intent

    final Uri theUri = FileProvider.getUriForFile(activity,
                        getString(R.string.fileprovider_authorities),
                        theFile);
    theIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
    theIntent.putExtra(MediaStore.EXTRA_OUTPUT, theUri);
    
于 2017-03-15T16:13:09.197 回答