Getting FileNotFoundException on Android app after

2019-07-10 05:30发布

问题:

I'm trying to to get an app to play an mp3 from the sd card. I'm using the android sdk version 23, with Android studio on Windows 8. I'm running this on the nexus 5 emulator.

I'm requesting permission at runtime and I'm getting a FileNotFoundException if I deny the permission once but than grant the permission on the second request. If I restart the app, I'm able to play the music file and if I accept the permission request on the first try it also successfully plays the music without requiring a restart.

I've made a simple program that replicates the problem and posted it on github. You need an mp3 file in the /Music dir to run this app.

Here's the code where I ask for storage permission:

 private void CheckPermission() {

    int permissionCheck = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE);

    if (permissionCheck == PackageManager.PERMISSION_GRANTED) {
        playSong();
    } else {
        // we don't have permission, request it
        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
                REQUEST_EXTERNAL_STORAGE_PERMISSION);

    }
}

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    switch (requestCode) {
        case REQUEST_EXTERNAL_STORAGE_PERMISSION:
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // Permission Granted
                Toast.makeText(getApplicationContext(), "Storage access granted, touch screen to start music", Toast.LENGTH_SHORT)
                        .show();
            } else {
                // Permission Denied
                Toast.makeText(getApplicationContext(), "Storage access denied, can't load music", Toast.LENGTH_SHORT)
                        .show();
            }
            break;
        default:
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }
}

Here's the relevant code snippet that's failing:

 Uri uri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
    String projection[] =
            {android.provider.MediaStore.Audio.Media.DATA, android.provider.MediaStore.Audio.Media.TITLE};
    Cursor cursor = this.getContentResolver().query(uri, projection, null, null, null);
    String songURI = new String();
    String title = new String();

    while (cursor.moveToNext()) {
        String data = cursor.getString(0);
        title = cursor.getString(1);
        // Note: Look for music folder in root drive.
        if (data.matches("^/storage/emulated/0/Music/.*")) {
            songURI = data;
            break;
        }
    }
    if (cursor != null) {
        cursor.close();
    }

    if (mediaPlayer != null) {
        mediaPlayer.stop();
        mediaPlayer.release();
        mediaPlayer = null;
    }

    mediaPlayer = MediaPlayer.create(getApplicationContext(), Uri.parse(songURI));

    try
    {
        mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
        mediaPlayer.start();
        Toast toast = Toast.makeText(getApplicationContext(), title, Toast.LENGTH_SHORT);
        toast.show();
    } catch (Exception e) {
        // TODO: handle exception
        e.printStackTrace();
        return;
    }

And here's stacktrace:

MediaPlayer: create failed: java.io.FileNotFoundException: /storage/emulated/0/Music/108-radiohead-house of cards-pms.mp3: open failed: EACCES (Permission denied) at libcore.io.IoBridge.open(IoBridge.java:452) at java.io.FileInputStream.(FileInputStream.java:76) at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1095) at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1074) at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1028) at android.media.MediaPlayer.setDataSource(MediaPlayer.java:973) at android.media.MediaPlayer.create(MediaPlayer.java:880) at android.media.MediaPlayer.create(MediaPlayer.java:857) at android.media.MediaPlayer.create(MediaPlayer.java:836) at gunboat.com.mediaplayererror.FullscreenActivity.playSong(FullscreenActivity.java:190) at gunboat.com.mediaplayererror.FullscreenActivity.CheckPermission(FullscreenActivity.java:135) at gunboat.com.mediaplayererror.FullscreenActivity.access$300(FullscreenActivity.java:24) at gunboat.com.mediaplayererror.FullscreenActivity$5.onClick(FullscreenActivity.java:113) at android.view.View.performClick(View.java:5198) at android.view.View$PerformClick.run(View.java:21147) at android.os.Handler.handleCallback(Handler.java:739) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:148) at android.app.ActivityThread.main(ActivityThread.java:5417) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) Caused by: android.system.ErrnoException: open failed: EACCES (Permission denied) at libcore.io.Posix.open(Native Method) at libcore.io.BlockGuardOs.open(BlockGuardOs.java:186) at libcore.io.IoBridge.open(IoBridge.java:438) at java.io.FileInputStream.(FileInputStream.java:76)  at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1095)  at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1074)  at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1028)  at android.media.MediaPlayer.setDataSource(MediaPlayer.java:973)  at android.media.MediaPlayer.create(MediaPlayer.java:880)  at android.media.MediaPlayer.create(MediaPlayer.java:857)  at android.media.MediaPlayer.create(MediaPlayer.java:836)  at gunboat.com.mediaplayererror.FullscreenActivity.playSong(FullscreenActivity.java:190)  at gunboat.com.mediaplayererror.FullscreenActivity.CheckPermission(FullscreenActivity.java:135)  at gunboat.com.mediaplayererror.FullscreenActivity.access$300(FullscreenActivity.java:24)  at gunboat.com.mediaplayererror.FullscreenActivity$5.onClick(FullscreenActivity.java:113)  at android.view.View.performClick(View.java:5198)  at android.view.View$PerformClick.run(View.java:21147)  at android.os.Handler.handleCallback(Handler.java:739)  at android.os.Handler.dispatchMessage(Handler.java:95)  at android.os.Looper.loop(Looper.java:148)  at android.app.ActivityThread.main(ActivityThread.java:5417) 

回答1:

Just dealt with this today. You need to restart your application after the permission is granted. I use something like this to accomplish the restart:

private static final int REQUEST_CODE_READ_EXTERNAL_STORAGE = 1;

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    switch (requestCode) {
        case REQUEST_CODE_READ_EXTERNAL_STORAGE:
            if (grantResults.length >= 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // Schedule start after 1 second
                PendingIntent pi = PendingIntent.getActivity(this, 0, getIntent(), PendingIntent.FLAG_CANCEL_CURRENT);
                AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
                am.set(AlarmManager.RTC, System.currentTimeMillis() + 1000, pi);

                // Stop now
                System.exit(0);
            } else {
                Toast.makeText(this, R.string.error_external_storage_unavailable, Toast.LENGTH_LONG).show();
                finish();
            }

            break;
    }

    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}

Here one source I used to come to this conclusion: Application not able to access SD card when WRITE_EXTERNAL_STORAGE permission is granted at run time

The reason for the restart is that when the READ_EXTERNAL_STORAGE or WRITE_EXTERNAL_STORAGE permissions are granted the user account for your application gets added to a new security group. This change doesn't take effect while the application process is still running.