Permiso READ_EXTERNAL_STORAGE para Android

84

Estoy intentando acceder a archivos multimedia (música) en el dispositivo del usuario para reproducirlos; una sencilla aplicación de reproductor de música "hola mundo".

He seguido algunos tutoriales y básicamente dan el mismo código. Pero no funcionará; sigue chocando y diciéndome:

error.....
Caused by: java.lang.SecurityException: Permission Denial: reading com.android.providers.media.MediaProvider uri content://media/external/audio/media from pid=27696, uid=10059 requires android.permission.READ_EXTERNAL_STORAGE, or grantUriPermission()
....

Ahora, este es mi archivo de manifiesto:

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

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


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

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>
</manifest>

Este es mi método Java:

public void initialize() {
    ContentResolver contentResolver = getContentResolver();
    Uri uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
    Cursor cursor = contentResolver.query(uri, null, null, null, null);
    if (cursor == null) {
        // query failed, handle error.
    } else if (!cursor.moveToFirst()) {
        // no media on the device
    } else {
        do {
            addSongToXML(cursor);
        } while (cursor.moveToNext());
    }
}

Yo he tratado:

Para poner esto en diferentes lugares del archivo de manifiesto:

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

Para agregar android: maxSdkVersion en Leer premisión de almacenamiento externo:

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

Para poner esto en el manifiesto / aplicación / etiqueta de actividad:

android:exported=“true

Para poner grantUriPremission entre uri y cursro en el método java:

grantUriPermission(null, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);

Para usar esto, no se bloqueará, pero el cursor se vuelve nulo:

uri = MediaStore.Audio.Media.getContentUri("EXTERNAL_CONTENT_URI”);

Para usar la URL de contenido INTERNO, esto funciona como se esperaba, pero solo proporciona "sonidos del sistema operativo", como el sonido del obturador, el sonido de batería baja, el clic del botón y demás:

uri = MediaStore.Audio.Media.INTERNAL_CONTENT_URI;

Por favor, ayuda, esto no debería ser un problema difícil, lo sé, ¡pero me siento como un principiante!

He leído y probado (o considero que no son aplicables para mi problema):

Seguimiento de pila:

09-08 06:59:36.619    2009-2009/slimsimapps.troff D/AndroidRuntime﹕ Shutting down VM
    --------- beginning of crash
09-08 06:59:36.619    2009-2009/slimsimapps.troff E/AndroidRuntime﹕ FATAL EXCEPTION: main
    Process: slimsimapps.troff, PID: 2009
    java.lang.IllegalStateException: Could not execute method for android:onClick
            at android.view.View$DeclaredOnClickListener.onClick(View.java:4452)
            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: java.lang.reflect.InvocationTargetException
            at java.lang.reflect.Method.invoke(Native Method)
            at android.view.View$DeclaredOnClickListener.onClick(View.java:4447)
            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: java.lang.SecurityException: Permission Denial: reading com.android.providers.media.MediaProvider uri content://media/external/audio/media from pid=2009, uid=10059 requires android.permission.READ_EXTERNAL_STORAGE, or grantUriPermission()
            at android.os.Parcel.readException(Parcel.java:1599)
            at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:183)
            at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:135)
            at android.content.ContentProviderProxy.query(ContentProviderNative.java:421)
            at android.content.ContentResolver.query(ContentResolver.java:491)
            at android.content.ContentResolver.query(ContentResolver.java:434)
            at slimsimapps.troff.MainActivity.initialize(MainActivity.java:106)
            at slimsimapps.troff.MainActivity.InitializeExternal(MainActivity.java:80)
            at java.lang.reflect.Method.invoke(Native Method)
            at android.view.View$DeclaredOnClickListener.onClick(View.java:4447)
            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)
    --------- beginning of system
Sim delgado
fuente
Es extraño que se muestre el error cuando no ha proporcionado esta línea en el manifiesto <uses-allow android: name = "android.permission.READ_EXTERNAL_STORAGE" /> ¿está intentando leer algunos archivos que están protegidos por otras aplicaciones?
sunil sunny
¿En qué nivel de API estás intentando esto?
Sharp Edge
¿Intentaste limpiar el proyecto? ?
sunil sunny
@sunilsunny No estoy tratando de leer algunos archivos que están protegidos, no que yo sepa de todos modos, solo un simple reproductor multimedia. Sí, he intentado limpiarlo, he intentado reiniciar la computadora, he intentado generar un APK firmado y publicarlo en Google Play y acceder a él como tester, sin suerte ....
Slim Sim
@Borde afilado ; Mi AVD es el Nexus 5 estándar, api 23. Mi módulo gradle tiene: compileSdkVersion 23 buildToolsVersion "23.0.0" minSdkVersion 14 targetSdkVersion 23 Así que yo diría 23.
Slim Sim

Respuestas:

90

Tienes dos soluciones para tu problema. La más rápida es reducir targetApi a 22 (archivo build.gradle). El segundo es utilizar un nuevo y maravilloso modelo de solicitud de permiso:

if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
        != PackageManager.PERMISSION_GRANTED) {

    // Should we show an explanation?
    if (shouldShowRequestPermissionRationale(
            Manifest.permission.READ_EXTERNAL_STORAGE)) {
        // Explain to the user why we need to read the contacts
    }

    requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
            MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE);

    // MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE is an
    // app-defined int constant that should be quite unique

    return;
}

Sniplet encontrado aquí: https://developer.android.com/training/permissions/requesting.html

Soluciones 2: Si no funciona, intente esto:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
    && ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
        REQUEST_PERMISSION);

return;

}

y luego en devolución de llamada

@Override
public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_PERMISSION) {
    if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
        // Permission granted.
    } else {
        // User refused to grant permission.
    }
}
}

eso es de los comentarios. Gracias

piotrpo
fuente
1
¡Sí, por supuesto! ¡El nuevo modelo de permisos que presentó Google con la última versión de Android! Gracias, lo probaré lo antes posible y lo marcaré como correcto si funciona. (¡y estaba empezando a pensar que esto se había olvidado!)
Slim Sim
1
El permiso que se solicita debería ser READ_EXTERNAL_STORAGE. Como en esta respuesta .
maclir
swap! = PackageManager.READ_EXTERNAL_STORAGE for! = PackageManager.PERMISSION_GRANTED
Renascienza
1
@Renascienza, sí tienes razón. Alguien editó esta respuesta de manera incorrecta.
piotrpo
2
¿Qué es MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE?
Solo
17

Tienes que pedir el permiso en tiempo de ejecución:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
        && ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
    ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
            REQUEST_PERMISSION);
    dialog.dismiss();
    return;
}

Y en la devolución de llamada a continuación, puede acceder al almacenamiento sin problemas.

@Override
public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == REQUEST_PERMISSION) {
        if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            // Permission granted.
        } else {
            // User refused to grant permission.
        }
    }
}
maclir
fuente
7

Paso 1: agregue permiso en android manifest.xml

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

Paso 2: método onCreate ()

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

    if (permissionCheck != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, MY_PERMISSIONS_REQUEST_READ_MEDIA);
    } else {
        readDataExternal();
    }

Paso 3: anule el método onRequestPermissionsResult para obtener la devolución de llamada

 @Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
    switch (requestCode) {
        case MY_PERMISSIONS_REQUEST_READ_MEDIA:
            if ((grantResults.length > 0) && (grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
                readDataExternal();
            }
            break;

        default:
            break;
    }
}

Nota: readDataExternal () es un método para obtener datos del almacenamiento externo.

Gracias.

sonnv1368
fuente
1
Creo que perdió el permiso necesario aquí: READ_EXTERNAL_STORAGE.
Renascienza
4

También tuve un registro de errores similar y esto es lo que hice:

  1. En el método onCreate solicitamos un cuadro de diálogo para verificar los permisos

    ActivityCompat.requestPermissions(MainActivity.this,
    new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},1);
    
  2. Método para verificar el resultado

    @Override
    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
    switch (requestCode) {
        case 1: {
            // If request is cancelled, the result arrays are empty.
            if (grantResults.length > 0
                    && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // permission granted and now can proceed
             mymethod(); //a sample method called
    
            } else {
    
                // permission denied, boo! Disable the
                // functionality that depends on this permission.
                Toast.makeText(MainActivity.this, "Permission denied to read your External storage", Toast.LENGTH_SHORT).show();
            }
            return;
        }
        // add other cases for more permissions
        }
    }
    

La documentación oficial para solicitar permisos en tiempo de ejecución


fuente
1
Lo usé y funcionó perfectamente, ¿funciona en todos los dispositivos Android?
Mahdi Hraybi
1

¿Se ha resuelto tu problema? ¿Cuál es su SDK de destino? Intenta agregar android;maxSDKVersion="21"a<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

Jerry Ejonavi
fuente
Me funcionó usando Cordova, estableciendo solo la versión máxima del SDK en AndroidManifest.xml en 22 (en lugar de 23)
George Kagan
0

http://developer.android.com/reference/android/provider/MediaStore.Audio.AudioColumns.html

Intenta cambiar la línea contentResolver.query

//change it to the columns you need
String[] columns = new String[]{MediaStore.Audio.AudioColumns.DATA}; 
Cursor cursor = contentResolver.query(uri, columns, null, null, null);

Cadena final estática pública MEDIA_CONTENT_CONTROL

No debe ser utilizado por aplicaciones de terceros debido a la privacidad del consumo de medios.

http://developer.android.com/reference/android/Manifest.permission.html#MEDIA_CONTENT_CONTROL

intente eliminar el <uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL"/>primero y vuelva a intentarlo?

Derek Fung
fuente
Gracias, intenté tu sugerencia pero falló en el mismo lugar ... Estoy usando una Mac, ¿puede ser un problema?
Slim Sim
actualizado, intente eliminar <uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL"/>y probar? Puede crear un comportamiento extraño porque la solicitud de su aplicación para un permiso no está permitida
Derek Fung
gracias, traté de eliminar ese permiso, pero se bloquea en el mismo lugar con el mismo error ...
Slim Sim
0

Por favor, verifique el código a continuación que al usarlo puede encontrar todos los archivos de música de sdcard:

public class MainActivity extends Activity{

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_animations);
    getAllSongsFromSDCARD();

}

public void getAllSongsFromSDCARD() {
    String[] STAR = { "*" };
    Cursor cursor;
    Uri allsongsuri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
    String selection = MediaStore.Audio.Media.IS_MUSIC + " != 0";

    cursor = managedQuery(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));
                System.out.println("sonng name"+fullpath);
            } while (cursor.moveToNext());

        }
        cursor.close();
    }
}


}

También agregué la siguiente línea en el archivo AndroidManifest.xml de la siguiente manera:

<uses-sdk
    android:minSdkVersion="16"
    android:targetSdkVersion="17" />

<uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Rajan Bhavsar
fuente
Gracias, he intentado algo similar antes, pero probé su código y falla en "cursor = managedQuery (allsongsuri, STAR, selection, null, null);", el mismo error que antes. Por cierto; ManagedQuery está en desuso, pero aún falla si lo cambio a "contentResolver.query", que tiene los mismos parámetros de entrada ...
Slim Sim
Hola, puedo obtener todas las canciones sdcadr usando el código anterior. con el dispositivo Android versión 4.2.2 y también con la versión 5.0.2. En el que estás intentando. Qué error obtuvo, por favor describa con la versión de Android
Rajan Bhavsar
He actualizado la pregunta con el seguimiento de la pila, espero que ayude. ¿Qué quieres decir con la versión de Android? (Estoy usando apk 23)
Slim Sim
Ok, déjeme dar el código fuente completo y verifique eso nuevamente.
Rajan Bhavsar
Si desea probarlo usted mismo, descargue el zip aquí: drive.google.com/open?id=0B2AV8VRk9HUeVzFXSWVRZU9TRUk cuando lo ejecute, presione el botón de inicialización izquierdo para generar el crach.
Slim Sim
0

Además de todas las respuestas. También puede especificar los minsdk para aplicar con esta anotación

@TargetApi(_apiLevel_)

Usé esto para aceptar esta solicitud, incluso mi minsdk es 18. Lo que hace es que el método solo se ejecuta cuando el dispositivo apunta a "_apilevel_" y superior. Este es mi método:

    @TargetApi(23)
void solicitarPermisos(){

    if (ContextCompat.checkSelfPermission(this,permiso)
            != PackageManager.PERMISSION_GRANTED) {

        // Should we show an explanation?
        if (shouldShowRequestPermissionRationale(
                Manifest.permission.READ_EXTERNAL_STORAGE)) {
            // Explain to the user why we need to read the contacts
        }

        requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
                1);

        // MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE is an
        // app-defined int constant that should be quite unique

        return;
    }

}
jan4co
fuente