Solicitar y permitir permiso WRITE_EXTERNAL_STORAGE en tiempo de ejecución no tiene efectos en la sesión actual
Esto se refiere al nuevo modelo de permisos de tiempo de ejecución introducido en Android Marshmallow al solicitar el permiso Manifest.permission.WRITE_EXTERNAL_STORAGE
.
En resumen, lo que estoy experimentando es que si solicito (y el usuario permite) el permiso Manifest.permission.WRITE_EXTERNAL_STORAGE
, la aplicación no podrá leer y escribir desde el directorio de almacenamiento externo hasta que destruya y reinicie la aplicación .
- Android: ¿cómo usar los colores del tema actual en un xml extraíble?
- Android: error al abrir el archivo de rastreo: ningún archivo o directorio de este tipo
- Tutorial de diseño de Android Runtime
- Adición / modificación de anotaciones en un proyecto java
- ¿Cómo puedo generar recursos XML en tiempo de ejecución en Android?
Esto es lo que estoy haciendo / experimentando:
Mi aplicación empieza en un estado en el que:
ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED
Esto es, no tengo permisos para acceder al almacenamiento externo.
Entonces, solicito permiso para Manifest.permission.WRITE_EXTERNAL_STORAGE tal como Google explica
private void requestWriteExternalStoragePermission() { // Should we show an explanation? if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { new AlertDialog.Builder(this) .setTitle("Inform and request") .setMessage("You need to enable permissions, bla bla bla") .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { ActivityCompat.requestPermissions(MendeleyActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, RC_PERMISSION_WRITE_EXTERNAL_STORAGE); } }) .show(); } else { ActivityCompat.requestPermissions(MendeleyActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, RC_PERMISSION_WRITE_EXTERNAL_STORAGE); } }
Una vez que el usuario permite el permiso, se invoca onRequestPermissionsResult
.
@Override public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { switch (requestCode) { case RC_PERMISSION_WRITE_EXTERNAL_STORAGE: { // If request is cancelled, the result arrays are empty. if (grantResults.length > 0 && PackageManager.PERMISSION_GRANTED // allowed } else { // denied } break; } } }
El bloque allowed
se ejecuta, confirmando que el usuario ha concedido permisos.
Inmediatamente después de esto, si no destruyo y vuelvo a abrir la aplicación, todavía no tengo permiso de acceso al almacenamiento externo. Más específicamente:
hasWriteExternalStoragePermission(); // returns true Environment.getExternalStorageDirectory().canRead(); // RETURNS FALSE!! Environment.getExternalStorageDirectory().canWrite(); // RETURNS FALSE!!
Por lo tanto, parece que el tiempo de ejecución de Android piensa que tengo permisos, pero el sistema de archivos no … De hecho, tratando de acceder a Environment.getExternalStorageDirectory()
lanza la excepción:
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.FileOutputStream.<init>(FileOutputStream.java:87) at java.io.FileOutputStream.<init>(FileOutputStream.java:72)
Si ahora destruyo la aplicación y la vuelvo a abrir , el comportamiento se vuelve como debería, pudiendo leer y escribir en la carpeta de almacenamiento externo.
¿Alguien está experimentando esto?
Estoy usando un emulador oficial con:
- Últimas API de Android 6.0 (API 23) 23, Rev 1.
- Emulador que ejecuta Intel x86 Atom System Image, API 23, Rev 1.
Construyo la aplicación con:
android { compileSdkVersion 23 buildToolsVersion "22.0.1" defaultConfig { minSdkVersion 16 targetSdkVersion 23 } ... }
Si alguien confirma esto y yo no soy el único, supongo que tendremos que abrir un error, pero espero que esté haciendo algo mal, ya que creo que una característica de este tipo es poco probable que sea buggy en el SDK.
- Problemas con ProGuard para Android con Parse 1.3.5 jar
- Runtime.exec (): ¿Reanudar en Android?
- Lenguaje basado en JVM sin tiempo de ejecución de lenguaje
- Ayuda de depuración de hilo (AsyncTask) RuntimeException causa en Android / Eclipse
- Android determina la orientación de la pantalla en tiempo de ejecución
- Android Runtime Logcat solo obtiene la instancia actual
http://developer.android.com/reference/android/Manifest.permission.html#WRITE_EXTERNAL_STORAGE :
A partir del nivel 19 de la API, este permiso no es necesario para leer / escribir archivos en los directorios específicos de la aplicación devueltos por getExternalFilesDir (String) y getExternalCacheDir ().
"La solicitud de permisos de tiempo de ejecución" comienza en el nivel 23 de la API, obviamente por encima de 19, por lo que el permiso ya no es necesario, a menos que accedas a los datos fuera de la carpeta señalada por getExternalFilesDir (). Así que creo que esto es un error del emulador.
En los objetivos inferiores por debajo del nivel 19, que no admiten el permiso solicitante en tiempo de ejecución, sólo calim el permiso en manifiesto y funcionará.
Yo tuve el mismo problema. Resulta que esto parece ser un problema más grande. Cambiar el permiso para escribir en el almacenamiento externo cambia el GID para este proceso (en el lado de linux). Para cambiar el ID, el proceso debe ser reiniciado. La próxima vez que abra la aplicación, se establecerá el nuevo ID de grupo y se concederá el permiso.
Larga historia corta, me temo que esto no es un error en el emulador, pero de hecho un problema más grande con Linux y Android.
He "resuelto" esto pidiendo permiso la primera vez que se ejecuta la aplicación y reiniciarla cuando el permiso se da así:
PackageManager packageManager = getPackageManager(); Intent intent = packageManager.getLaunchIntentForPackage(getPackageName()); ComponentName componentName = intent.getComponent(); Intent mainIntent = IntentCompat.makeRestartActivityTask(componentName); startActivity(mainIntent); System.exit(0);
Puede intentar crear un servicio que se ejecute en segundo plano (con otro id de proceso) y darle el permiso. De esta manera sólo tendría que reiniciar el servicio y no la aplicación completa. En el lado de abajo esto podría hacer más trabajo para usted.
Espero que esto ayude.
— EDITAR —
El usuario M66B ( https://stackoverflow.com/a/32473449/1565635 ) encontró una lista de los gids relacionados. Puede encontrar más información en: https://android.googlesource.com/platform/frameworks/base/+/master/data/etc/platform.xml