安卓读取外部存储出现SecurityException问题

7

我正在尝试获取设备中的音乐文件,以下是我的操作:

public ArrayList<Track> getAllSongsFromSDCARD()
{
    allTracks = new ArrayList<Track>();
    String[] STAR = { "*" };
    Uri allsongsuri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
    String selection = MediaStore.Audio.Media.IS_MUSIC + " != 0";

    Cursor cursor = mContext.getApplicationContext().getContentResolver().query(allsongsuri, STAR, selection, null, null);

    if (cursor != null) {
        if (cursor.moveToFirst()) {
            do {
                String song_name = cursor
                        .getString(cursor
                                .getColumnIndex(MediaStore.Audio.Media.DISPLAY_NAME));
                int song_id = cursor.getInt(cursor
                        .getColumnIndex(MediaStore.Audio.Media._ID));

                String fullpath = cursor.getString(cursor
                        .getColumnIndex(MediaStore.Audio.Media.DATA));


                String album_name = cursor.getString(cursor
                        .getColumnIndex(MediaStore.Audio.Media.ALBUM));
                int album_id = cursor.getInt(cursor
                        .getColumnIndex(MediaStore.Audio.Media.ALBUM_ID));

                String artist_name = cursor.getString(cursor
                        .getColumnIndex(MediaStore.Audio.Media.ARTIST));
                int artist_id = cursor.getInt(cursor
                        .getColumnIndex(MediaStore.Audio.Media.ARTIST_ID));



                String duration = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DURATION));
                int[] splitTime = null;
                String hours, minutes, seconds, formattedTime;
                try{
                    splitTime = splitToComponentTimes(BigDecimal.valueOf(Long.valueOf(duration)/1000));
                    hours = String.valueOf(splitTime[0]);
                    minutes = String.valueOf(splitTime[1]);
                    seconds = String.valueOf(splitTime[2]);
                    if(hours.equals("0")){
                        formattedTime = minutes + ":" + seconds;
                    }else{
                        formattedTime = hours + ":" + minutes + ":" + seconds;
                    }
                }catch (Exception e){
                    formattedTime = "00:00";
                }

                allTracks.add(new Track(song_name, song_id, fullpath, album_name, album_id, artist_name, artist_id, formattedTime));
            } while (cursor.moveToNext());

        }

        sortTracksAlphabetically(allTracks);
        cursor.close();
    }
    return allTracks;
}

而且我在我的清单中拥有以下权限:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

当我在安卓版本为4.3的设备上运行该代码时,它能够正常工作。但是当我在安卓版本为5.0.2的设备上运行时,它会报错:
"java.lang.SecurityException: Permission Denial: reading com.android.providers.media.MediaProvider uri content://media/external/audio/media from pid=5701, uid=10054 requires android.permission.READ_EXTERNAL_STORAGE"
有谁能告诉我为什么会这样呢?以下是完整的错误追踪信息:
02-14 19:25:52.812    5701-5701/com.yrazlik.musicplayer E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: com.yrazlik.musicplayer, PID: 5701
java.lang.SecurityException: Permission Denial: reading com.android.providers.media.MediaProvider uri content://media/external/audio/media from pid=5701, uid=10054 requires android.permission.READ_EXTERNAL_STORAGE, or grantUriPermission()
        at android.os.Parcel.readException(Parcel.java:1540)
        at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:185)
        at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:137)
        at android.content.ContentProviderProxy.query(ContentProviderNative.java:420)
        at android.content.ContentResolver.query(ContentResolver.java:478)
        at android.content.ContentResolver.query(ContentResolver.java:422)
        at com.yrazlik.musicplayer.dialogs.AllSongsDialog.getAllSongsFromSDCARD(AllSongsDialog.java:74)
        at com.yrazlik.musicplayer.dialogs.AllSongsDialog.onCreate(AllSongsDialog.java:59)
        at android.app.Dialog.dispatchOnCreate(Dialog.java:373)
        at android.app.Dialog.show(Dialog.java:274)
        at com.yrazlik.musicplayer.fragments.PlaylistFragment$1.onClick(PlaylistFragment.java:86)
        at android.view.View.performClick(View.java:4756)
        at android.view.View$PerformClick.run(View.java:19749)
        at android.os.Handler.handleCallback(Handler.java:739)
        at android.os.Handler.dispatchMessage(Handler.java:95)
        at android.os.Looper.loop(Looper.java:135)
        at android.app.ActivityThread.main(ActivityThread.java:5221)
        at java.lang.reflect.Method.invoke(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:372)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)

感谢您的选择。

1
请确保您的<uses-permission>元素被正确放置。它们需要作为<manifest>的直接子元素,与<application>同级。 - CommonsWare
@CommonsWare 你说得对,我将权限放在application标签内了,现在我将它们移动到了正确的位置。现在可以正常工作了。但是为什么我的Android 4.3设备没有问题,而Android 5.0.2设备却有问题呢? - yrazlik
2个回答

9
我在清单文件中有以下权限:
经常情况下,即使在清单文件中有元素,如果这些元素放置在错误的位置,仍然会出现权限错误。它们需要作为的直接子代,与同级。
但是为什么我的Android 4.3设备没有问题,而在5.0.2设备上却有问题呢?
在Android 4.4之前,READ_EXTERNAL_STORAGE要么不存在,要么不会在普通设备上执行。

我在Android 6上遇到了这个问题,但权限是正确的地方。 - nAkhmedov
5
您遇到的问题很可能与运行时权限有关。@nAkhmedov提供了一个解决方法链接:https://dev59.com/fFwY5IYBdhLWcg3waXS5 - CommonsWare
非常感谢 @nAkhmedov,我在构建版本23上遇到了问题,你发布的链接真的帮了我很大的忙。 - M090009
@CommonsWare 当在 Android 23 模拟器上运行您的 Leanback/VideoBrowse 示例时,实际上会发生这种情况。 - IgorGanapolsky
2
@IgorGanapolsky:“在Marshmallow上,清单权限与运行时权限无关”--当然有关。运行时权限始于在清单中拥有权限。除此之外,该示例项目的targetSdkVersion低于23,因此在任何Android版本上都不使用运行时权限系统。 Android 6.0设备对待这样的旧应用程序与旧设备一样,关于权限(所有权限都提前授予)。 - CommonsWare
显示剩余3条评论

2

以下是“CommonsWare”在其他链接上的非常好的回答:

现在无法获取您的权限的主要原因是,您的项目具有目标SdkVersion 23或更高版本,并且您正在请求的权限是“危险”的。在Android 6.0中,这包括:

ACCESS_COARSE_LOCATION
ACCESS_FINE_LOCATION
ADD_VOICEMAIL
BODY_SENSORS
CALL_PHONE
CAMERA
GET_ACCOUNTS
PROCESS_OUTGOING_CALLS
READ_CALENDAR
READ_CALL_LOG
READ_CELL_BROADCASTS
READ_CONTACTS
READ_EXTERNAL_STORAGE
READ_PHONE_STATE
READ_SMS
RECEIVE_MMS
RECEIVE_SMS
RECEIVE_WAP_PUSH
RECORD_AUDIO
SEND_SMS
USE_SIP
WRITE_CALENDAR
WRITE_CALL_LOG WRITE_CONTACTS
WRITE_EXTERNAL_STORAGE

对于这些权限,不仅您的targetSdkVersion 23+应用程序需要具有<uses-permission>元素,而且您还必须在Android 6.0+设备上从用户处请求运行时权限,使用checkSelfPermission()和requestPermissions()等方法。

作为临时解决方法,请将目标SdkVersion降低到23以下。

但是,最终,您将有一些原因希望您的targetSdkVersion为23或更高版本。那时,您将需要调整您的应用程序以使用新的运行时权限系统。 Android 6.0预览文档有一页专门介绍此主题。

现在我将我的目标SdkVersion从23降级到21,它可以工作,但这是一个临时解决方案。


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