Android.os.FileUriExposedException: archivo: ///storage/emulated/0/test.txt expuesto más allá de la aplicación a través de Intent.getData ()

La aplicación se bloquea al intentar abrir un archivo. Funciona debajo de Android N, pero en Android N se bloquea. Sólo se bloquea cuando intento abrir un archivo desde la tarjeta SD, no desde la partición del sistema. ¿Algún problema de permiso?

Código de muestra:

File file = new File("/storage/emulated/0/test.txt"); Intent intent = new Intent(Intent.ACTION_VIEW); intent.setDataAndType(Uri.fromFile(file), "text/*"); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); // Crashes on this line 

Iniciar sesión:

 android.os.FileUriExposedException: file:///storage/emulated/0/test.txt exposed beyond app through Intent.getData() 

Editar:

Al orientar Android N, file:// URIs ya no están permitidos. Deberíamos usar content:// URIs en su lugar. Sin embargo, mi aplicación necesita abrir archivos en directorios raíz. ¿Algunas ideas?

Si su targetSdkVersion es 24 o superior, tenemos que usar la clase FileProvider para dar acceso al archivo o carpeta en particular para hacerlos accesibles para otras aplicaciones. Creamos nuestra propia clase heredando FileProvider para asegurarnos de que FileProvider no entre en conflicto con FileProviders declarados en dependencias importadas como se describe aquí .

Pasos para reemplazar file: // uri con contenido: // uri:

  • Agregue una clase extendiendo FileProvider

     public class GenericFileProvider extends FileProvider { } 
  • Agregue una etiqueta FileProvider en AndroidManifest.xml bajo etiqueta. Especifique una autoridad única para el atributo android:authorities para evitar conflictos, las dependencias importadas podrían especificar ${applicationId}.provider y otras autoridades de uso común.

 <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" ... <application ... <provider android:name=".GenericFileProvider" android:authorities="${applicationId}.my.package.name.provider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/provider_paths"/> </provider> </application> </manifest> 
  • A continuación, cree un archivo provider_paths.xml en la carpeta xml en la carpeta res . La carpeta puede ser necesaria para crear si no existe. A continuación se muestra el contenido del archivo. Describe que nos gustaría compartir el acceso a la carpeta de almacenamiento externo en la carpeta raíz (path=".") Con el nombre external_files .
 <?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <external-path name="external_files" path="."/> </paths> 
  • El paso final es cambiar la línea de código abajo en

     Uri photoURI = Uri.fromFile(createImageFile()); 

    a

     Uri photoURI = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".my.package.name.provider", createImageFile()); 

Espero que esto ayude … 🙂

Por favor, consulte, el código completo y la solución se ha explicado aquí.

Además de la solución que utiliza el FileProvider , hay otra forma de evitar esto. Simplemente pon

 StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder(); StrictMode.setVmPolicy(builder.build()); 

En Application.onCreate() . De esta forma, la VM ignora la exposición URI del archivo.

Método

 builder.detectFileUriExposure() 

Activa la comprobación de la exposición del archivo, que también es el comportamiento predeterminado si no configuramos VmPolicy.

Me encontré con un problema que si utilizo un content:// URI para enviar algo, algunas aplicaciones simplemente no pueden entenderlo. Y la degradación de la versión del target SDK no está permitida. En este caso mi solución es útil.

Si su targetSdkVersion es 24 o superior, no puede utilizar file: valores de file: Uri en Intents en dispositivos Android 7.0+ .

Sus opciones son:

  1. Suelte su targetSdkVersion a 23 o menos, o

  2. Coloque su contenido en el almacenamiento interno, a continuación, utilice FileProvider para hacerlo disponible de forma selectiva a otras aplicaciones

Por ejemplo:

 Intent i=new Intent(Intent.ACTION_VIEW, FileProvider.getUriForFile(this, AUTHORITY, f)); i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); startActivity(i); 

(De este ejemplo de proyecto )

Primero tienes que añadir un proveedor a tu AndroidManifest

  <application ...> <activity> .... </activity> <provider android:name="android.support.v4.content.FileProvider" android:authorities="com.your.package.fileProvider" android:grantUriPermissions="true" android:exported="false"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider> </application> 

Ahora crear un archivo en la carpeta de recursos xml (si utiliza estudio android puede pulsar Alt + Intro después de resaltar los caminos_archivos y seleccione crear una opción de recurso xml)

A continuación, en el archivo file_paths ingrese

 <?xml version="1.0" encoding="utf-8"?> <paths> <external-path path="Android/data/com.your.package/" name="files_root" /> <external-path path="." name="external_storage_root" /> </paths> 

Este ejemplo es para la ruta externa que puede consultar aquí para más opciones. Esto le permitirá compartir archivos que están en esa carpeta y su subcarpeta.

Ahora todo lo que queda es crear la intención de la siguiente manera:

  MimeTypeMap mime = MimeTypeMap.getSingleton(); String ext = newFile.getName().substring(newFile.getName().lastIndexOf(".") + 1); String type = mime.getMimeTypeFromExtension(ext); try { Intent intent = new Intent(); intent.setAction(Intent.ACTION_VIEW); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); Uri contentUri = FileProvider.getUriForFile(getContext(), "com.your.package.fileProvider", newFile); intent.setDataAndType(contentUri, type); } else { intent.setDataAndType(Uri.fromFile(newFile), type); } startActivityForResult(intent, ACTIVITY_VIEW_ATTACHMENT); } catch (ActivityNotFoundException anfe) { Toast.makeText(getContext(), "No activity found to open this attachment.", Toast.LENGTH_LONG).show(); } 

EDIT : He añadido la carpeta raíz de la tarjeta sd en el file_paths. He probado este código y funciona.

Si su aplicación apunta a la API 24+ y todavía desea / necesita usar file: // intents, puede usar hacky para deshabilitar la comprobación de tiempo de ejecución:

 if(Build.VERSION.SDK_INT>=24){ try{ Method m = StrictMode.class.getMethod("disableDeathOnFileUriExposure"); m.invoke(null); }catch(Exception e){ e.printStackTrace(); } } 

El método StrictMode.disableDeathOnFileUriExposure está oculto y documentado como:

 /** * Used by lame internal apps that haven't done the hard work to get * themselves off file:// Uris yet. */ 

El problema es que mi aplicación no es cojo, pero no quiere ser lisiado mediante el uso de contenido: / / intentos que no son entendidos por muchas aplicaciones por ahí. Por ejemplo, abrir el archivo mp3 con el contenido: // esquema ofrece mucho menos aplicaciones que cuando se abre el mismo archivo: // esquema. No quiero pagar por los fallos de diseño de Google limitando la funcionalidad de mi aplicación.

Google quiere que los desarrolladores utilicen el esquema de contenido, pero el sistema no está preparado para esto, durante años se hicieron aplicaciones para utilizar archivos no "contenido", los archivos se pueden editar y guardar de nuevo, mientras que los archivos servidos sobre el esquema de contenido no puede ser ¿ellos?).

@palash k respuesta es correcta y trabajado para los archivos de almacenamiento interno, pero en mi caso quiero abrir archivos de almacenamiento externo también, mi aplicación se estrelló al abrir el archivo de almacenamiento externo como sdcard y usb, pero logro resolver el problema mediante la modificación Provider_paths.xml de la respuesta aceptada

Cambie el provider_paths.xml como a continuación

 <?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <external-path path="Android/data/${applicationId}/" name="files_root" /> <root-path name="root" path="/" /> </paths> 

Y en la clase java (No hay cambio como la respuesta aceptada sólo una pequeña edición)

 Uri uri=FileProvider.getUriForFile(getActivity(), BuildConfig.APPLICATION_ID+".provider", File) 

Esto me ayuda a arreglar el bloqueo de archivos de almacenamiento externo, Espero que esto ayude a alguien que tiene el mismo problema que el mío 🙂

Usar el fileProvider es el camino a seguir. Pero puede utilizar esta solución sencilla:

ADVERTENCIA : Se solucionará en la próxima versión de Android: https://issuetracker.google.com/issues/37122890#comment4

reemplazar:

 startActivity(intent); 

por

 startActivity(Intent.createChooser(intent, "Your title")); 

Utilicé la respuesta de Palash dada arriba pero era algo incompleta, tuve que proporcionar el permiso como esto

 Intent intent = new Intent(Intent.ACTION_VIEW); Uri uri; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { uri = FileProvider.getUriForFile(this, getPackageName() + ".provider", new File(path)); List<ResolveInfo> resInfoList = getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); for (ResolveInfo resolveInfo : resInfoList) { String packageName = resolveInfo.activityInfo.packageName; grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION); } }else { uri = Uri.fromFile(new File(path)); } intent.setDataAndType(uri, "application/vnd.android.package-archive"); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); 

Si targetSdkVersion es mayor que 24 , FileProvider se utiliza para conceder acceso.

Agregar un proveedor en AndroidManifest.xml

  <provider android:name="android.support.v4.content.FileProvider" android:authorities="${applicationId}.provider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/provider_paths"/> </provider> 

Y reemplazar

 Uri uri = Uri.fromFile(fileImagePath); 

a

 Uri uri = FileProvider.getUriForFile(MainActivity.this, BuildConfig.APPLICATION_ID + ".provider",fileImagePath); 

Y usted es bueno para ir.

  • Xamarin C # Android 7.0 en Moto G5 Plus teléfono, ventana de diálogo fragmento de fondo transparente procesamiento de error
  • La barra de estado del turrón de Android muestra color blanco al iniciar una actividad
  • Java.lang.SecurityException: MODE_WORLD_READABLE ya no es compatible
  • Android - El idioma de WebView cambia abruptamente en Android N
  • No puedo desplazar mi diseño en multi-ventana en Nougat 7.0
  • Expand / Collapse Animación de la barra de herramientas de Lollipop (Telegram app)
  • Selendroid no puede reconocer el dispositivo Android 7
  • Cómo abrir un archivo APK para todas las versiones de Android
  • Intención de instalar el archivo APK - Android Nougat
  • Android Nougat PopupWindow showAsDropDown (...) La gravedad no funciona
  • Android onStop () no se llama al iniciar una nueva actividad en modo multi-ventana
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.