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


738

La aplicación se bloquea cuando intento abrir un archivo. Funciona debajo de Android Nougat, pero en Android Nougat se bloquea. Solo 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: archivo: ///storage/emulated/0/test.txt expuesto más allá de la aplicación a través de Intent.getData ()

Editar:

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


20
Siento que esto fue un error que dificulta la vida innecesariamente para los desarrolladores de aplicaciones. Tener que agrupar un "FileProvider" y una "autoridad" con cada aplicación, parece un ejemplo de Enterprisey. Tener que agregar una bandera a cada intento de archivo parece incómodo y posiblemente innecesario. Romper el elegante concepto de "caminos" es desagradable. ¿Y cuál es el beneficio? ¿Otorgar selectivamente acceso de almacenamiento a aplicaciones (mientras que la mayoría de las aplicaciones tienen acceso completo a tarjetas SD, especialmente las que funcionan en archivos)?
nyanpasu64

2
intente esto, código pequeño y perfecto stackoverflow.com/a/52695444/4997704
Binesh Kumar

Respuestas:


1316

Si es así targetSdkVersion >= 24, entonces tenemos que usar la FileProviderclase para dar acceso al archivo o carpeta en particular para que sean accesibles para otras aplicaciones. Creamos nuestra propia clase heredando FileProviderpara asegurarnos de que nuestro FileProvider no entre en conflicto con FileProviders declarado en dependencias importadas como se describe aquí .

Pasos para reemplazar file://URI con content://URI:

  • Agregar una clase que se extienda FileProvider

    public class GenericFileProvider extends FileProvider {}
  • Agregue una <provider>etiqueta FileProvider AndroidManifest.xmldebajo de la <application>etiqueta. Especifique una autoridad única para el android:authoritiesatributo para evitar conflictos, las dependencias importadas pueden especificar ${applicationId}.providery 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}.provider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths"/>
        </provider>
    </application>
</manifest>
  • Luego cree un provider_paths.xmlarchivo en la res/xmlcarpeta. Es posible que se necesite crear una carpeta si no existe. El contenido del archivo se muestra a continuación. Describe que nos gustaría compartir el acceso al 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 a continuación en

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

    a

    Uri photoURI = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".provider", createImageFile());
  • Editar: si está utilizando una intención para hacer que el sistema abra su archivo, es posible que deba agregar la siguiente línea de código:

    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

Consulte, el código completo y la solución se han explicado aquí.


62
Solo necesitaba agregar intent.setFlags (Intent.FLAG_GRANT_READ_URI_PERMISSION);
alorma

24
¿Funcionará para todas las versiones de Android, o solo desde API 24?
Desarrollador de Android


11
@rockhammer Acabo de probar esto con Android 5.0, 6.0, 7.1 y 8.1, funciona en todos los casos. Entonces la (Build.VERSION.SDK_INT > M)condición es inútil.
Sébastien

66
FileProviderdebe extenderse solo si desea anular cualquiera de los comportamientos predeterminados; de lo contrario, use android:name="android.support.v4.content.FileProvider". Ver developer.android.com/reference/android/support/v4/content/…
JP Ventura

313

Además de la solución que usa FileProvider, hay otra forma de solucionar esto. Simplemente pon

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

en Application.onCreate(). De esta manera, la VM ignora la URIexposición del archivo .

Método

builder.detectFileUriExposure()

habilita la comprobación de exposición de archivos, que también es el comportamiento predeterminado si no configuramos una VmPolicy.

Encontré un problema que si uso un content:// URIpara enviar algo, algunas aplicaciones simplemente no pueden entenderlo. Y target SDKno se permite degradar la versión. En este caso mi solución es útil.

Actualizar:

Como se mencionó en el comentario, StrictMode es una herramienta de diagnóstico y no se debe utilizar para este problema. Cuando publiqué esta respuesta hace un año, muchas aplicaciones solo pueden recibir archivos uris. Simplemente se bloquean cuando intento enviarles un uri de FileProvider. Esto se soluciona en la mayoría de las aplicaciones ahora, por lo que deberíamos ir con la solución FileProvider.


1
@LaurynasG De API 18 a 23, Android no comprueba la exposición de uri de archivo de forma predeterminada. Llamar a este método habilita esta verificación. Desde API 24, Android realiza esta verificación de forma predeterminada. Pero podemos desactivarlo configurando uno nuevo VmPolicy.
hqzxzwb

¿Hay algún otro paso necesario para que esto funcione? No funciona como está para mi Moto G con Android 7.0.
CKP78

3
Sin embargo, ¿cómo puede resolver este problema, StrictMode es una herramienta de diagnóstico que debe habilitarse en el modo de desarrollador, no en el modo de lanzamiento?
Imene Noomene

1
@ImeneNoomene En realidad, estamos desactivando StrictMode aquí. Parece razonable que StrictMode no se habilite en el modo de lanzamiento, pero de hecho, Android habilita algunas opciones de StrictMode de forma predeterminada, independientemente del modo de depuración o el modo de lanzamiento. Pero de una forma u otra, esta respuesta estaba destinada a ser una solución alternativa cuando algunas aplicaciones de destino no estaban preparadas para recibir contenido uris. Ahora que la mayoría de las aplicaciones han agregado soporte para uris de contenido, deberíamos usar el patrón FileProvider.
hqzxzwb

3
@ImeneNoomene Estoy totalmente contigo en tu indignación. Tienes razón, esta es una herramienta de diagnóstico, o al menos lo fue hace años cuando la agregué a mis proyectos. ¡Esto es súper frustrante! StrictMode.enableDefaults();, que ejecuto solo en mis compilaciones de desarrollo evita que ocurra este bloqueo, por lo que ahora tengo una aplicación de producción que se bloquea pero no se bloquea cuando está en desarrollo. Básicamente, habilitar una herramienta de diagnóstico aquí oculta un problema grave. Gracias @hqzxzwb por ayudarme a desmitificar esto.
Jon

174

Si targetSdkVersiones superior a 24 , FileProvider se utiliza para otorgar acceso.

Cree un archivo xml (Ruta: res \ xml) provider_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_files" path="."/>
</paths>


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>

Si está utilizando androidx , la ruta de FileProvider debe ser:

 android:name="androidx.core.content.FileProvider"

y reemplazar

Uri uri = Uri.fromFile(fileImagePath);

a

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

Editar: mientras incluye el URI, Intentasegúrese de agregar la siguiente línea:

intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

y eres bueno para ir Espero eso ayude.


2
@MaksimKniazev ¿Puede describir su error brevemente? Para que pueda ayudarte.
Pankaj Lilan

1
@PankajLilan, hice exactamente lo que dijiste. Pero cada vez que abro mi pdf en la otra aplicación, aparece en blanco (se guarda correctamente). ¿Debería editar el xml? Ya agregué FLAG_GRANT_READ_URI_PERMISSION también;
Felipe Castilhos

1
Mi error, estaba agregando el permiso a la intención equivocada. Esta es la mejor y más simple respuesta correcta. ¡Gracias!
Felipe Castilhos

2
Lanza la excepción java.lang.IllegalArgumentException: no se pudo encontrar la raíz configurada que contiene / La ruta de mi archivo es /storage/emulated/0/GSMManage_DCIM/Documents/Device7298file_74.pdf. ¿puedes por favor ayudarme?
Jagdish Bhavsar

1
No sé por qué, pero tuve que agregar los permisos READ y WRITE: target.addFlags (Intent.FLAG_GRANT_READ_URI_PERMISSION) target.addFlags (Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
caja del

160

Si su aplicación está dirigida a API 24+, y aún quiere / necesita usar file: // intentos, puede usar una forma hacky para deshabilitar la verificació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.disableDeathOnFileUriExposureestá 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 aburrida, sino que no quiere quedar paralizada al usar content: // intenciones que muchas aplicaciones no entienden. Por ejemplo, abrir un archivo mp3 con content: // esquema ofrece muchas menos aplicaciones que al abrirlo sobre file: // esquema. No quiero pagar las fallas de diseño de Google limitando la funcionalidad de mi aplicación.

Google quiere que los desarrolladores usen el esquema de contenido, pero el sistema no está preparado para esto, durante años las aplicaciones se hicieron para usar Archivos que no son "contenido", los archivos se pueden editar y guardar de nuevo, mientras que los archivos que se sirven sobre el esquema de contenido no se pueden (pueden ¿ellos?).


3
"mientras que los archivos servidos sobre el esquema de contenido no pueden ser (¿o sí?"). - claro, si tienes acceso de escritura al contenido. ContentResolvertiene ambos openInputStream()y openOutputStream(). Una forma menos hacky de hacerlo es configurar las reglas de VM usted mismo y no habilitar la file Uriregla.
CommonsWare

1
Exactamente. Es difícil trabajar cuando compiló toda su aplicación, luego descubra que después de apuntar a 25 se rompen todos sus métodos de cámara. Esto funciona para mí hasta que tenga tiempo para hacerlo de la manera correcta.
Matt W

55
Funciona en Android 7. Gracias
Anton Kizema

44
También funciona en Android 8, probado en Huawei Nexus 6P.
Gonzalo Ledezma Torres

44
Confirmo que esto está funcionando en la producción (tengo más de 500,000 usuarios), actualmente la versión 8.1 es la versión más alta y funciona en ella.
Eli

90

Si targetSdkVersiontiene 24 o más, no puede usar file: Urivalores en Intentsdispositivos Android 7.0+ .

Sus elecciones son:

  1. Caída de su targetSdkVersiona 23 o menos, o

  2. Ponga su contenido en almacenamiento interno, luego úseloFileProvider para que esté disponible selectivamente para 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 proyecto de muestra )


Gracias por la respuesta. ¿Qué sucede cuando uso esto con archivos en la /systempartición? Todas las aplicaciones deberían poder acceder a esta partición sin root.
Thomas Vos

2
@SuperThomasLab: No contaría con todo para /systemser legible en todo el mundo. Dicho esto, supongo que todavía obtendrás esta excepción. Sospecho que solo están revisando el esquema y no están tratando de determinar si el archivo es realmente legible en todo el mundo. Sin embargo, FileProviderno te ayudará, ya que no puedes enseñarle a servir /system. Podrías crear una estrategia personalizada para miStreamProvider , o rodar la tuya ContentProvider, para superar el problema.
CommonsWare

Todavía estoy pensando cómo voy a resolver esto. La aplicación que estoy actualizando con compatibilidad con Android N es un navegador raíz. Pero ahora ya no puede abrir ningún archivo en los directorios raíz. ( /data, /system), debido a este "buen cambio".
Thomas Vos

1
¿Cuáles son los inconvenientes más importantes para dejar targetSdkVersion a 23? thnx
rommex

2
@rommex: No sé qué califica como "más importante". Por ejemplo, a los usuarios que trabajan en modo de pantalla dividida o en dispositivos de ventanas múltiples de forma libre (Chromebooks, Samsung DeX) se les informará que su aplicación puede no funcionar con ventanas múltiples. Si eso es importante o no, depende de usted.
CommonsWare

47

Primero debe agregar un proveedor a su 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 cree un archivo en la carpeta de recursos xml (si usa android studio puede presionar Alt + Enter después de resaltar file_paths y seleccionar crear una opción de recurso xml)

Luego 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 una ruta externa que puede consultar aquí para obtener 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();
    }

EDITAR : agregué la carpeta raíz de la tarjeta SD en file_paths. He probado este código y funciona.


1
Gracias por esto. También quiero hacerle saber que hay una mejor manera de obtener la extensión del archivo. String extension = android.webkit.MimeTypeMap.getFileExtensionFromUrl(Uri.fromFile(file).toString()); Además, recomiendo a todos los que busquen respuestas que lean primero FileProvider y entiendan a qué se enfrentan aquí con los permisos de archivos en Android N y superiores. Hay opciones de almacenamiento interno frente a almacenamiento externo y también para rutas de archivos normales frente a rutas de caché.
praneetloke

2
Estaba recibiendo la siguiente excepción: java.lang.IllegalArgumentException: Failed to find configured root ...y lo único que funcionó fue <files-path path="." name="files_root" />en el archivo xml en lugar de <external-path .... Mi archivo se guardó en el almacenamiento interno.
steliosf

26

La respuesta de @palash k es correcta y funcionó para archivos de almacenamiento interno, pero en mi caso también quiero abrir archivos de almacenamiento externo, mi aplicación se bloqueó cuando abrí un archivo de almacenamiento externo como sdcard y usb, pero logré resolver el problema modificando proveedor_rutass.xml de la respuesta aceptada

cambiar 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 (Sin cambios ya que la respuesta aceptada es solo una pequeña edición)

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

Esto me ayuda a solucionar el bloqueo de archivos de almacenamientos externos, espero que esto ayude a alguien que tenga el mismo problema que el mío :)


1
¿Dónde encontraste por <root-pathfavor? Esta funcionando. <external-path path="Android/data/${applicationId}/" name="files_root" />no tuvo ningún efecto para los archivos abiertos del almacenamiento externo.
t0m

Encuentro esto en varios resultados de búsqueda, déjame volver a consultar y volver a consultarlo lo antes posible
Ramz

¿También el almacenamiento externo que menciona es tarjeta SD o almacenamiento incorporado?
Ramz

Perdón por la inexactitud. Android/data/${applicationId}/Quise decir en SDcard.
T0M

1
Necesita agregar esto a la intención: intent.addFlags (Intent.FLAG_GRANT_READ_URI_PERMISSION);
s-hunter

26

Mi solución fue 'Uri.parse' la ruta del archivo como cadena, en lugar de usar Uri.fromFile ().

String storage = Environment.getExternalStorageDirectory().toString() + "/test.txt";
File file = new File(storage);
Uri uri;
if (Build.VERSION.SDK_INT < 24) {
    uri = Uri.fromFile(file);
} else {
    uri = Uri.parse(file.getPath()); // My work-around for new SDKs, doesn't work in Android 10.
}
Intent viewFile = new Intent(Intent.ACTION_VIEW);
viewFile.setDataAndType(uri, "text/plain");
startActivity(viewFile);

Parece que fromFile () usa un puntero de archivo A, lo que supongo que podría ser inseguro cuando las direcciones de memoria están expuestas a todas las aplicaciones. Pero una cadena de ruta de archivo nunca hace daño a nadie, por lo que funciona sin lanzar FileUriExposedException.

Probado en los niveles API 9 a 27! Abre con éxito el archivo de texto para editarlo en otra aplicación. No requiere FileProvider, ni la biblioteca de soporte de Android en absoluto.


Desearía haber visto esto primero. No probé que funcionó para mí, pero es mucho menos engorroso que FileProvider.
Dale

Una nota sobre por qué esto realmente funciona: no es el puntero del archivo el que causa el problema, sino el hecho de que la excepción solo ocurre si tiene una ruta con 'file: //', que se antepone automáticamente con fromFile, pero no con parse .
Xmister

3
Esto no tiene excepción, pero tampoco puede enviar el archivo a la aplicación relacionada. Entonces, no funcionó para mí.
Serdar Samancıoğlu

1
Esto fallará en Android 10 y superior, ya que no puede asumir que la otra aplicación tiene acceso al almacenamiento externo a través del sistema de archivos.
CommonsWare

24

Simplemente pegue el siguiente código en la actividad onCreate ()

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

Ignorará la exposición a URI


1
Esta es una de las soluciones pero no la estándar. Las personas que votaron negativamente por las respuestas están equivocadas, ya que este también es un código de trabajo con la solución de trabajo.
saksham

23

Simplemente pegue el siguiente código en actividad onCreate().

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

Ignorará la exposición a URI.

Feliz codificación :-)


1
¿Cuáles son las desventajas de esto?
James F

1
Esto fallará en Android 10 y superior, ya que no puede asumir que la otra aplicación tiene acceso al almacenamiento externo a través del sistema de archivos.
CommonsWare

18

Usar el fileProvider es el camino a seguir. Pero puede usar esta solución simple:

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"));

77
El selector será revisado pronto por Google para contener la misma verificación. Esta no es una solución.
Puntero nulo

Éste funciona pero no funcionará en futuras versiones de Android.
Diljeet

13

Utilicé la respuesta de Palash dada anteriormente, pero estaba algo incompleta, tuve que proporcionar un permiso como este

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);

11

Simplemente pegue el siguiente código en la actividad onCreate ()

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

Ignorará la exposición a URI


Esto, esto eliminará las políticas de modo estricto. e ignorará la advertencia de seguridad. No es una buena solución.
inspire_coding

También fallará en Android 10 y superior, ya que no puede asumir que la otra aplicación tiene acceso al almacenamiento externo a través del sistema de archivos.
CommonsWare

7

agregue estas dos líneas en onCreate

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

Método de compartir

File dir = new File(Environment.getExternalStorageDirectory(), "ColorStory");
File imgFile = new File(dir, "0.png");
Intent sendIntent = new Intent(Intent.ACTION_VIEW);
sendIntent.setType("image/*");
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://" + imgFile));
sendIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(Intent.createChooser(sendIntent, "Share images..."));

Esto fallará en Android 10 y superior, ya que no puede asumir que la otra aplicación tiene acceso al almacenamiento externo a través del sistema de archivos.
CommonsWare

7

Aquí mi solución:

en Manifiesto.xml

<application
            android:name=".main.MainApp"
            android:allowBackup="true"
            android:icon="@drawable/ic_app"
            android:label="@string/application_name"
            android:logo="@drawable/ic_app_logo"
            android:theme="@style/MainAppBaseTheme">

        <provider
                android:name="androidx.core.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>

en res / xml / provider_paths.xml

   <?xml version="1.0" encoding="utf-8"?>
    <paths xmlns:android="http://schemas.android.com/apk/res/android">
        <external-path name="external_files" path="."/>
    </paths>

en mi fragmento tengo el siguiente código:

 Uri myPhotoFileUri = FileProvider.getUriForFile(getActivity(), getActivity().getApplicationContext().getPackageName() + ".provider", myPhotoFile);               
    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    intent.putExtra(MediaStore.EXTRA_OUTPUT, myPhotoFileUri);

Eso es todo lo que necesitas.

Tampoco es necesario crear

public class GenericFileProvider extends FileProvider {}

Pruebo en Android 5.0, 6.0 y Android 9.0 y es un trabajo exitoso.


He probado esta solución y funciona bien con un pequeño cambio: intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK intent.putExtra (Intent.EXTRA_STREAM, myPhotoFileUri) intent.type = "image / png" startActivity (Intent.createChooser (intent, " Compartir imagen a través de ")) Funciona fin en Android 7 y 8.
inspire_coding

4

Para descargar pdf del servidor, agregue el código a continuación en su clase de servicio. Espero que esto te ayude.

File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), fileName + ".pdf");
    intent = new Intent(Intent.ACTION_VIEW);
    //Log.e("pathOpen", file.getPath());

    Uri contentUri;
    contentUri = Uri.fromFile(file);
    intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);

    if (Build.VERSION.SDK_INT >= 24) {

        Uri apkURI = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".provider", file);
        intent.setDataAndType(apkURI, "application/pdf");
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

    } else {

        intent.setDataAndType(contentUri, "application/pdf");
    }

Y sí, no olvide agregar permisos y proveedor en su manifiesto.

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

<application

<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>

</application>

1
lo que es @xml/provider_paths?
adityasnl

1
@Heisenberg, consulte la publicación de Rahul Upadhyay desde url: stackoverflow.com/questions/38555301/…
Bhoomika Chauhan

3

No sé por qué, hice todo exactamente igual que Pkosta ( https://stackoverflow.com/a/38858040 ) pero seguí recibiendo errores:

java.lang.SecurityException: Permission Denial: opening provider redacted from ProcessRecord{redacted} (redacted) that is not exported from uid redacted

Perdí horas en este tema. ¿El culpable? Kotlin

val playIntent = Intent(Intent.ACTION_VIEW, uri)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)

intenten realidad estaba configurando en getIntent().addFlagslugar de operar en mi playIntent recientemente declarado.


2

Puse este método para que la ruta imageuri ingrese fácilmente al contenido.

enter code here
public Uri getImageUri(Context context, Bitmap inImage)
{
    ByteArrayOutputStream bytes = new ByteArrayOutputStream();
    inImage.compress(Bitmap.CompressFormat.PNG, 100, bytes);
    String path = MediaStore.Images.Media.insertImage(context.getContentResolver(), 
    inImage, "Title", null);
    return Uri.parse(path);
}

2
As of Android N, in order to work around this issue, you need to use the FileProvider API

Hay 3 pasos principales aquí como se menciona a continuación

Paso 1: entrada manifiesta

<manifest ...>
    <application ...>
        <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>
    </application>
</manifest>

Paso 2: Crear un archivo XML res / xml / provider_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_files" path="."/>
</paths>

Paso 3: cambios de código

File file = ...;
Intent install = new Intent(Intent.ACTION_VIEW);
install.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
// Old Approach
    install.setDataAndType(Uri.fromFile(file), mimeType);
// End Old approach
// New Approach
    Uri apkURI = FileProvider.getUriForFile(
                             context, 
                             context.getApplicationContext()
                             .getPackageName() + ".provider", file);
    install.setDataAndType(apkURI, mimeType);
    install.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
// End New Approach
    context.startActivity(install);

1

Sé que esta es una pregunta bastante antigua, pero esta respuesta es para futuros espectadores. Entonces encontré un problema similar y después de investigar, encontré una alternativa a este enfoque.

Su intención aquí, por ejemplo: para ver su imagen desde su camino en Kotlin

 val intent = Intent()
 intent.setAction(Intent.ACTION_VIEW)
 val file = File(currentUri)
 intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
 val contentURI = getContentUri(context!!, file.absolutePath)
 intent.setDataAndType(contentURI,"image/*")
 startActivity(intent)

Función principal a continuación

private fun getContentUri(context:Context, absPath:String):Uri? {
        val cursor = context.getContentResolver().query(
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
            arrayOf<String>(MediaStore.Images.Media._ID),
            MediaStore.Images.Media.DATA + "=? ",
            arrayOf<String>(absPath), null)
        if (cursor != null && cursor.moveToFirst())
        {
            val id = cursor.getInt(cursor.getColumnIndex(MediaStore.MediaColumns._ID))
            return Uri.withAppendedPath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, Integer.toString(id))
        }
        else if (!absPath.isEmpty())
        {
            val values = ContentValues()
            values.put(MediaStore.Images.Media.DATA, absPath)
            return context.getContentResolver().insert(
                MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
        }
        else
        {
            return null
        }
    }

Del mismo modo, en lugar de una imagen, puede usar cualquier otro formato de archivo como pdf y, en mi caso, funcionó bien


0

Pasé casi un día tratando de descubrir por qué recibía esta excepción. Después de mucha lucha, esta configuración funcionó perfectamente ( Kotlin ):

AndroidManifest.xml

<provider
  android:name="androidx.core.content.FileProvider"
  android:authorities="com.lomza.moviesroom.fileprovider"
  android:exported="false"
  android:grantUriPermissions="true">
  <meta-data
    android:name="android.support.FILE_PROVIDER_PATHS"
    android:resource="@xml/file_paths" />
</provider>

file_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths>
  <files-path name="movies_csv_files" path="."/>
</paths>

Intención en sí

fun goToFileIntent(context: Context, file: File): Intent {
    val intent = Intent(Intent.ACTION_VIEW)
    val contentUri = FileProvider.getUriForFile(context, "${context.packageName}.fileprovider", file)
    val mimeType = context.contentResolver.getType(contentUri)
    intent.setDataAndType(contentUri, mimeType)
    intent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION

    return intent
}

Explico todo el proceso aquí .


-1

https://stackoverflow.com/a/38858040/395097 esta respuesta está completa.

Esta respuesta es para: ya tiene una aplicación que estaba dirigida a menos de 24, y ahora está actualizando a targetSDKVersion> = 24.

En Android N, solo se cambia el archivo uri expuesto a la aplicación de terceros. (No de la forma en que lo estábamos usando antes). Así que cambie solo los lugares donde comparte la ruta con la aplicación de terceros (Cámara en mi caso)

En nuestra aplicación, estábamos enviando uri a la aplicación de la cámara, en esa ubicación esperamos que la aplicación de la cámara almacene la imagen capturada.

  1. Para Android N, generamos nuevo contenido: // url basada en uri que apunta al archivo.
  2. Generamos la ruta basada en la API del archivo habitual para el mismo (utilizando un método anterior).

Ahora tenemos 2 uri diferentes para el mismo archivo. # 1 se comparte con la aplicación de la cámara. Si la intención de la cámara es exitosa, podemos acceder a la imagen desde el n. ° 2.

Espero que esto ayude.


1
Está haciendo referencia a una respuesta ya publicada aquí, si necesita completarla, comente en la respuesta por favor.
IgniteCoders

1
@IgniteCoders Como mencioné claramente en el mensaje, mi respuesta cubre el caso de uso relacionado.
Aram

-1

Xamarin.Android

Nota: La ruta xml / provider_paths.xml (.axml) no se pudo resolver, incluso después de hacer la carpeta xml en Recursos (tal vez se puede colocar en una ubicación existente como Valores , no lo intenté), así que recurrí a esto que funciona por ahora. Las pruebas mostraron que solo es necesario invocarlo una vez por ejecución de la aplicación (lo cual tiene sentido ya que cambia el estado operativo de la VM del host).

Nota: xml debe estar en mayúscula, por lo que Resources / Xml / provider_paths.xml

Java.Lang.ClassLoader cl = _this.Context.ClassLoader;
Java.Lang.Class strictMode = cl.LoadClass("android.os.StrictMode");                
System.IntPtr ptrStrictMode = JNIEnv.FindClass("android/os/StrictMode");
var method = JNIEnv.GetStaticMethodID(ptrStrictMode, "disableDeathOnFileUriExposure", "()V");                
JNIEnv.CallStaticVoidMethod(strictMode.Handle, method);

-1

La respuesta de @Pkosta es una forma de hacerlo.

Además de usar FileProvider, también puede insertar el archivo MediaStore(especialmente para archivos de imagen y video), porque los archivos en MediaStore son accesibles para todas las aplicaciones:

MediaStore está dirigido principalmente a tipos MIME de video, audio e imagen, sin embargo, a partir de Android 3.0 (nivel de API 11) también puede almacenar tipos no multimedia (consulte MediaStore.Files para obtener más información). Los archivos se pueden insertar en MediaStore usando scanFile () después de lo cual se pasa un contenido: // estilo Uri adecuado para compartir a la devolución de llamada proporcionada onScanCompleted (). Tenga en cuenta que una vez agregado al sistema MediaStore, el contenido es accesible para cualquier aplicación en el dispositivo.

Por ejemplo, puede insertar un archivo de video en MediaStore como este:

ContentValues values = new ContentValues();
values.put(MediaStore.Video.Media.DATA, videoFilePath);
Uri contentUri = context.getContentResolver().insert(
      MediaStore.Video.Media.EXTERNAL_CONTENT_URI, values);

contentUries como content://media/external/video/media/183473, que se puede pasar directamente a Intent.putExtra:

intent.setType("video/*");
intent.putExtra(Intent.EXTRA_STREAM, contentUri);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
activity.startActivity(intent);

Esto funciona para mí y ahorra las molestias de usar FileProvider.


-1

Simplemente deje que ignore la exposición a URI ... Agréguelo después de crear

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

Esto fallará en Android 10 y superior, ya que no puede asumir que la otra aplicación tiene acceso al almacenamiento externo a través del sistema de archivos.
CommonsWare

Esto no debe usarse en una aplicación de producción.
Jorgesys

-1

Prueba esta solución

PONER ESTOS PERMISOS EN MANIFIESTO

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

Intento de capturar imagen

Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
                    startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
                }

OBTENGA LA IMAGEN CAPTURADA EN EL RESULTADO DE LA ACTIVIDAD

@Override
            protected void onActivityResult(int requestCode, int resultCode, Intent data) {
                super.onActivityResult(requestCode, resultCode, data);
                if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
                    Bundle extras = data.getExtras();
                    Bitmap imageBitmap = (Bitmap) extras.get("data");
                    // CALL THIS METHOD TO GET THE URI FROM THE BITMAP
                    Uri tempUri = getImageUri(getApplicationContext(), imageBitmap);
                    //DO SOMETHING WITH URI
                }
            } 

MÉTODO PARA OBTENER URI DE IMAGEN

public Uri getImageUri(Context inContext, Bitmap inImage) {
        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
        inImage.compress(Bitmap.CompressFormat.JPEG, 100, bytes);
        String path = MediaStore.Images.Media.insertImage(inContext.getContentResolver(), inImage, "Title", null);
        return Uri.parse(path);
    }

¿Alguien puede decirme por qué votar abajo? Esta es una solución 100% funcional.
Abdul Basit Rishi

Esto solo te da la miniatura, no la imagen completa.
Build3r

-2

En mi caso, eliminé la excepción reemplazando SetDataAndTypepor just SetData.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.