Android无法在dir中创建文件getExternalStoragePublicDirectory()

Dan*_*noz 5 android

我正在尝试通过以下解释此代码将我自己的警报音复制到Android系统.

问题是我无法将文件从原始资源复制到外部存储器.我在这里看了其他问题,但似乎没有答案对我有用.

这是我的AndroidManifest(我把WRITE_EXTERNAL_STORAGE外应用程序标记为说明这里):

...
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

<application...
Run Code Online (Sandbox Code Playgroud)

这是我的代码.它运行在从我的主要活动的onCreate开始的新线程上.从该新线程调用方法"void onFirstTime()",并且此方法重复调用"void copyRawRingtone(File dstPath,int resId)":

void copyRawRingtone(File dstPath, int resId) {
    final String fileName = getResources().getResourceEntryName(resId) + ".mp3";
    File dstFile = new File(dstPath, fileName);
    if (!dstFile.exists()) {
        InputStream srcStream = getResources().openRawResource(resId);
        FileOutputStream dstStream = null;
        try {
            dstFile.createNewFile(); // THIS THROWS THE EXCEPTION
            dstStream = new FileOutputStream(dstFile);
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = srcStream.read(buffer)) > 0) {
                dstStream.write(buffer, 0, bytesRead);
            }

            // Set the file metadata
            final String mimeType = "audio/mpeg";
            final String dstAbsPath = dstFile.getAbsolutePath();
            ContentValues contentValues = new ContentValues();
            contentValues.put(MediaStore.MediaColumns.DATA, dstAbsPath);
            contentValues.put(MediaStore.MediaColumns.TITLE, fileName);
            contentValues.put(MediaStore.MediaColumns.MIME_TYPE, mimeType);
            contentValues.put(MediaStore.Audio.Media.IS_ALARM, true);
            contentValues.put(MediaStore.Audio.Media.IS_NOTIFICATION, false);
            contentValues.put(MediaStore.Audio.Media.IS_RINGTONE, false);
            contentValues.put(MediaStore.Audio.Media.IS_MUSIC, false);

            // Add the metadata to the file in the database
            Uri contentUri = MediaStore.Audio.Media.getContentUriForPath(dstAbsPath);
            Uri newUri = getContentResolver().insert(contentUri, contentValues);

            // Tell the media scanner about the new ringtone
            MediaScannerConnection.scanFile(
                    this,
                    new String[]{newUri.toString()},
                    new String[]{mimeType},
                    null
            );
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (dstStream != null) {
                try {
                    dstStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        try {
            srcStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

// First time initialization (called in a new thread from onCreate)
void onFirstTime() {
    if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
        File dstPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_ALARMS);
        // This returns false but since I run it on an emulator, I don't know how to check if it actually exists:
        dstPath.mkdirs();
        Field[] rawResources = R.raw.class.getFields();
        final ProgressBar installProgress = (ProgressBar) findViewById(R.id.progressBar);
        final int length = rawResources.length;
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                installProgress.setMax(length);
            }
        });
        for (int i = 0; i < length; ++i) {
            try {
                int resId = rawResources[i].getInt(rawResources[i]);
                copyRawRingtone(dstPath, resId); // Call the method above
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            final int progress = i + 1;
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    installProgress.setProgress(progress);
                }
            });
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

行:dstFile.createNewFile(); 抛出异常:

java.io.IOException: open failed: ENOENT (No such file or directory)
Run Code Online (Sandbox Code Playgroud)

我不明白可能是什么问题.我在模拟器上运行它:Nexus_S_API_21_armeabi-v7a.

我没有在我的电脑下看到模拟器的存储,所以我认为手机模拟器存储没有安装在我的电脑上,所以我不认为这可能是问题所在.

在异常时刻,这些是一些变量值:

dstPath="/storage/sdcard/Alarms"
fileName="whistle_alarm.mp3"
dstFile="/storage/sdcard/Alarms/whistle_alarm.mp3"
Run Code Online (Sandbox Code Playgroud)

因为这返回true:

Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)
Run Code Online (Sandbox Code Playgroud)

似乎媒体已挂载且可写(不是MEDIA_MOUNTED_READ_ONLY).所以我不知道会出现什么问题!有没有人有任何想法?

编辑

我添加了这两行(我还在前面放了一行,后面有一行在上下文中看到它:

File dstPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_ALARMS);
        final boolean exists = dstPath.exists(); // Added. exists=true
        final boolean canWrite = dstPath.canWrite(); // Added. canWrite=true
        dstPath.mkdirs();
Run Code Online (Sandbox Code Playgroud)

我也删除了:

dstFile.createNewFile(); // THIS THROWS THE EXCEPTION
Run Code Online (Sandbox Code Playgroud)

它现在有效!

但是我没有使用这个createNewFile()行多次测试过它并没有用.确实,我添加了这一行,看看我是否可以使用它.所以我再试一次这条线,它也有效!

我怀疑实际的解决方案是重启模拟器.