java.lang.IllegalArgumentException: 找不到包含 /storage/emulated/0/Pictures/ 的配置根目录。

15
寻求帮助,我在开发一个App时尝试存储相机拍摄的照片时遇到了错误。 错误为
java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/0/Pictures/JPEG20161108_153704_ logcat指向代码中调用FileProvider.getUriForFile的行。
private void dispatchTakePhoto() {
        Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        startActivity(takePictureIntent); // this worked originally
        if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
            File photoFile = null;
            try {
                photoFile = createImageFile();
            } catch (IOException e) {
                e.printStackTrace();
                Log.e(TAG, ""+e);
            }
            if (photoFile != null) {
                Uri photoURI = FileProvider.getUriForFile(TallyActivity2.this,
                        "com.example.bigdaddy.pipelinepipetally.fileprovider", photoFile);
                takePictureIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
                startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
            }
        }
    }

该方法用于创建图像文件

 private File createImageFile() throws IOException {
        /* Create an image file name */
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
        String imageFileName = "JPEG" + timeStamp + "_";
        File storageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).getAbsoluteFile(), imageFileName);
        File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);

        /* Tried this also, not working. Leaving for debugging.
        File image = File.createTempFile(
                imageFileName,
                ".jpg",
                storageDir
        );*/

        File image = new File(path, imageFileName);
        try {
            /* Making sure the Pictures directory exist.*/
            path.mkdir();
            storageDir.createNewFile();
        }catch (Exception e) {
            e.printStackTrace();
        }

        /* Save a file: path for use with ACTION_VIEW intents */
        mCurrentPhotoPath = "file:" + image.getAbsolutePath();
        return image;
    }

这里是onActivityResult()方法,我想保存通过 saveImage() 方法捕获的图像到一个类中,并将缩略图设置为ImageViewsaveImage()方法返回一个字节,因此我可以通过意图(Bundle)将该字节传递到另一个全屏活动中,在那里如果用户点击缩略图ImageView将显示它。

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        /* If it's equal to my REQUEST_IMAGE_CAPTURE var, we are all good. */
        if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
            Bundle extras = data.getExtras();
            Bitmap imageBitmap = (Bitmap) extras.get("data");
            /*Saving to the Pipe class with the saveImage() method below.*/
            sDummyImagePicByte = saveImage(imageBitmap);
            /* Going ahead an setting the thumbnail here for the picture taken*/
            mPipePicImage.setImageBitmap(imageBitmap);
            Log.i(TAG,Arrays.toString(sDummyImagePicByte)+" after assignment from saveImage()");
        }
    }

这里是Manifest.xml文件。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          xmlns:tools="http://schemas.android.com/tools"
          package="com.example.bigdaddy.pipelinepipetally">

    <uses-permission android:name="android.permission.READ_CONTACTS"/>
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
        android:maxSdkVersion="18"/>
    <uses-permission android:name="android.permission.CAMERA"/>
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"/>

    <application
        android:allowBackup="true"
        android:debuggable="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme"
        tools:ignore="HardcodedDebugMode">
        <activity
            android:name=".MainActivity"
            android:windowSoftInputMode="adjustResize">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <activity
            android:name=".TallyActivity2"
            android:windowSoftInputMode="adjustResize">
        </activity>
        <activity
            android:name=".JobAndDbActivity"
            android:windowSoftInputMode="adjustResize">
        </activity>
        <activity
            android:name=".ExistingTallyActivity"
            android:windowSoftInputMode="adjustResize">
        </activity>
        <activity android:name=".ImageToFullscreen"
            android:windowSoftInputMode="adjustResize">
        </activity>

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

    </application>
</manifest>

这是我创建的file_paths.xml文件,并将其放在app/res/xml/文件夹中(我也创建了该文件夹)。不确定这是否是正确的位置(对于该文件夹)。

<paths >
    <files-path name="my_images" path="files/"/>
    ...
</paths>

这也是onRequestPermissionsResult()函数。

 @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[],
                                           @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode) {
            case MY_PERMISSIONS_REQUEST_CAMERA:
                /* if request is canceled, the result arrays are empty */
                if(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
                    /* Permissions granted so the mPermissionIsGranted boolean is set to true*/
                    mPermissionIsGranted = true;
                } else {
                    /*
                    Permissions denied so the mPermissionIsGranted boolean stays false here and
                    providing a Toast message to the user, letting them know that camera permissions
                    are required for this feature.
                    */
                    Toast.makeText(getApplicationContext(),"Camera permissions required\nfor this" +
                                    "feature.",
                            Toast.LENGTH_LONG).show();
                    /* Continuing to hold the false setting to this boolean since not granted.*/
                    mPermissionIsGranted = false;
                }
                break;
            /* For accessing and writing to the SD card*/
            case MY_PERMISSIONS_REQUEST_SD_CARD:
                /* if request is canceled, the result arrays are empty */
                if(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
                    /* Permissions granted so the mPermissionIsGranted boolean is set to true*/
                    mPermissionIsGranted = true;
                } else  {
                    /*
                    Permissions denied so the mPermissionIsGranted boolean stays false here and
                    providing a Toast message to the user, letting them know that camera permissions
                    are required for this feature.
                    */
                    Toast.makeText(getApplicationContext(),"SD Card permissions required\nfor this"+
                                    "feature.",
                            Toast.LENGTH_LONG).show();
                    /* Continuing to hold the false setting to this boolean since not granted.*/
                    mPermissionIsGranted = false;
                }
                break;
            /* For the GPS location permissions.*/
            default: MY_PERMISSIONS_REQUEST_FINE_LOCATION:
            /* Still to be implemented .*/
                break;
        }
    }

非常感谢您的帮助。我还是新手,正在学习Android。提前谢谢。

5个回答

15

以前我为File提供者的所有路径都有样板代码。你永远不会遇到这种类型的错误。

<?xml version="1.0" encoding="utf-8"?>
<paths>
  <external-path name="external" path="." />
  <external-files-path name="external_files" path="." />
  <cache-path name="cache" path="." />
  <external-cache-path name="external_cache" path="." />
  <files-path name="files" path="." />
</paths>

欲了解更多细节,请查看FileProvider-指定可用文件


仍然存在问题。文件夹路径为/Download/ - Srikar Reddy
3
如何修复文件夹路径为/Download/的问题? - Manikandan K

14

有点晚了,但我终于解决了。

根据文档的说法:路径组件仅对应于使用Environment.DIRECTORY_PICTURES调用getExternalFilesDir() 返回的路径。

因此请使用

getExternalFilesDir(Environment.DIRECTORY_PICTURES)

而不是

Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)

你的路径应该像这样:

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-path name="InstructiveRide" path="Android/data/com.package.ride/files/Pictures"/>
</paths>

如果您的“图片”目录下有子目录,请按照以下方式编写代码:

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-path name="InstructiveRide" path="Android/data/com.package.ride/files/Pictures/InstructiveRide/"/>
</paths>

1
为什么这不是被接受的答案?它是唯一一个起作用的... - LPains
1
谢谢!你的文档比谷歌的好多了.... - shagberg

5

我也在尝试升级我的应用程序以避免FileUriExposedException。我想实现与实际问题中提到的相同的目标。我成功地使用了Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)。

唯一的技巧是使用以下路径:

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-path name="external_files" path="." />
</paths>

我从下面的博客中获得了这个提示。

https://proandroiddev.com/sharing-files-though-intents-are-you-ready-for-nougat-70f7e9294a0b

我花了几个小时才弄清楚。我希望它能帮助节省其他人的时间。


5
<files-path name="my_images" path="files/"/>

这指的是getFilesDir()内的files/目录。然而,createImageFile()不是在寻找将文件放置的地方。相反,它使用了Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)。您需要:

  1. 决定哪个位置是正确的(或选择其他选项),然后

  2. 同步实现


@J2112O:正确。请参考文档 - CommonsWare
嗨 @CommonsWare ,所以 @J2112O 需要将 <files-path name="my_images" path="files/"/> 改为 <external-path name="my_images" path="files/"/>files/ 路径是正确的吗?还有其他需要更改的地方吗? - Jaco
我看到了@CommonsWare,谢谢!所以@J2112O需要使用getFilesDir()而不是Environment.getExternalStoragePublicDirectory(Environment.DI‌​RECTORY_PICTURES),使用files/路径。正如您在此社区上所说,这是一个文档错误,对吗?https://developer.android.com/training/camera/photobasics.html 谢谢! - Jaco
1
@Jaco:切换到getFilesDir()会是最简单的方法。你可以尝试黑客攻击一个<external-path>来指向DIRECTORY_PICTURES,但我担心它会不可靠。<external-path>文档错误已在几个月前修复。 - CommonsWare
1
我也卡在这个问题上了,你解决了吗?我已经尝试过 getFilesDir()<files-path name="my_images" path="files/"/> 但都没有成功。 - lost baby
显示剩余3条评论

2
我的问题是我在build.gradle文件中为我的应用程序id定义了一个后缀,在调试模式下运行应用程序时,包名与我在代码中提到的不兼容。更好的方法是,在您的xmljava代码中设置应用程序ID(包名)变量。要在提供者中执行此操作,请在您的清单中设置:
android:authorities="${applicationId}.fileprovider"

在你的Java代码中:

Uri photoURI = FileProvider.getUriForFile(context,
                    getApplicationContext().getPackageName()+".fileprovider",

对于您的提供者路径,请创建两个不同的文件以适应不同的构建变体(发布版和调试版)

针对发布版:

<external-path name="my_images" path="Android/data/com.android.example/files/Pictures" />

同时用于调试:

<external-path name="my_images" path="Android/data/com.android.example.debug/files/Pictures" />

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接