fileprovider - android take picture programmatically
Posible utilizar mĂșltiples autoridades con FileProvider? (2)
Mi solución a este problema realmente ha sido evitar confiar en un único FileProvider
analizando múltiples autoridades. Si bien esto no aborda directamente la pregunta como se indica, lo estoy publicando para la posteridad.
FileProvider
mi biblioteca para aprovechar una subclase vacía de FileProvider
, de modo que la entrada del proveedor de manifiesto actualizado de la biblioteca ahora es:
<provider
android:name=".flow.email.screenshot.BugShakerFileProvider"
android:authorities="${applicationId}.bugshaker.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/library_file_paths" />
</provider>
El manifiesto combinado de una aplicación que (1) utiliza un FileProvider
archivo y (2) consume mi biblioteca ahora contendrá las dos entradas que se muestran a continuación (¡no hay colisión!):
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.consuming.application.fileprovider"
android:exported="false"
android:grantUriPermissions="true" >
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/application_file_paths" />
</provider>
<provider
android:name="com.github.stkent.bugshaker.flow.email.screenshot.BugShakerFileProvider"
android:authorities="com.consuming.application.bugshaker.fileprovider"
android:exported="false"
android:grantUriPermissions="true" >
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/library_file_paths" />
</provider>
No me di cuenta de que esta era una solución potencial hasta que un compañero de trabajo lo señaló. Mi suposición había sido previamente (e incorrectamente) que todos los FileProvider
s en el manifiesto deben establecer
android:name="android.support.v4.content.FileProvider"
Pero una revisión rápida de la documentación reveló mi error:
El nombre de la clase que implementa el proveedor de contenido, una subclase de ContentProvider. Este debe ser un nombre de clase completo (como "com.example.project.TransportationProvider"). [...]
Fondo
Mantengo una library cuya funcionalidad principal consiste en compartir capturas de pantalla capturadas mediante programación a aplicaciones de correo electrónico externas.
Utilizo un FileProvider
para lograr esto, lo que significa que el manifiesto de mi biblioteca contiene una etiqueta <provider>
:
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.bugshaker.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths" />
</provider>
filepaths.xml
se define de la siguiente manera:
<paths>
<files-path path="bug-reports/" name="bug-reports" />
</paths>
Un consumidor de mi biblioteca tiene una aplicación que usa un FileProvider
para compartir archivos. Mi expectativa era que debería ser posible permitir que ambos proveedores compartan archivos si la aplicación consumidora utiliza la siguiente etiqueta de manifiesto <provider>
:
<provider
android:authorities="${applicationId}.fileprovider;${applicationId}.bugshaker.fileprovider"
android:exported="false"
android:grantUriPermissions="true"
android:name="android.support.v4.content.FileProvider"
tools:replace="android:authorities">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"
tools:replace="android:resource" />
</provider>
Esta entrada manifiesta:
- especifica dos autoridades del
Provider
,${applicationId}.fileprovider
(para compartir archivos de la aplicación) y${applicationId}.bugshaker.fileprovider
(para compartir archivos de la biblioteca); - hace referencia a
filepaths.xml
actualizado, que contiene definiciones de directorios independientes para los archivos generados por la aplicación y los archivos generados por la biblioteca:
<paths>
<external-path
name="redacted"
path="" />
<files-path
name="bug-reports"
path="bug-reports/" />
</paths>
Después de crear la aplicación, hemos confirmado que el manifiesto generado ha tenido los nodos correctos reemplazados con estos valores actualizados.
Sin embargo, cuando la aplicación que utiliza esta configuración se ensambla (correctamente) y se ejecuta, vemos un bloqueo en el inicio:
E: FATAL EXCEPTION: main
Process: com.stkent.bugshakertest, PID: 11636
java.lang.RuntimeException: Unable to get provider android.support.v4.content.FileProvider: java.lang.NullPointerException: Attempt to invoke virtual method ''android.content.res.XmlResourceParser android.content.pm.PackageItemInfo.loadXmlMetaData(android.content.pm.PackageManager, java.lang.String)'' on a null object reference
at android.app.ActivityThread.installProvider(ActivityThread.java:5856)
at android.app.ActivityThread.installContentProviders(ActivityThread.java:5445)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:5384)
at android.app.ActivityThread.-wrap2(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1545)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6119)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method ''android.content.res.XmlResourceParser android.content.pm.PackageItemInfo.loadXmlMetaData(android.content.pm.PackageManager, java.lang.String)'' on a null object reference
at android.support.v4.content.FileProvider.parsePathStrategy(FileProvider.java:583)
at android.support.v4.content.FileProvider.getPathStrategy(FileProvider.java:557)
at android.support.v4.content.FileProvider.attachInfo(FileProvider.java:375)
at android.app.ActivityThread.installProvider(ActivityThread.java:5853)
at android.app.ActivityThread.installContentProviders(ActivityThread.java:5445)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:5384)
at android.app.ActivityThread.-wrap2(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1545)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6119)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
Al usar el depurador, puedo ver que el método FileProvider.parsePathStrategy
invoca PackageManager.resolveContentProvider
con la cadena de autoridad "${applicationId}.fileprovider;${applicationId}.bugshaker.fileprovider"
. resolveContentProvider
devuelve un valor nulo, lo que lleva a este NPE.
Si llamo a resolveContentProvider
manualmente mientras resolveContentProvider
una pausa en esta instrucción y paso "${applicationId}.fileprovider"
o "${applicationId}.bugshaker.fileprovider"
, resolveContentProvider
devuelve una instancia de ProviderInfo
no nula (lo que parece ser la esperada resultado).
Esta diferencia me confunde porque la documentación del elemento <provider>
indica que se admiten varias autoridades:
Una lista de una o más autoridades URI que identifican los datos ofrecidos por el proveedor de contenido. Se enumeran varias autoridades separando sus nombres con un punto y coma. Para evitar conflictos, los nombres de autoridad deben usar una convención de nomenclatura de estilo Java (como com.example.provider.cartoonprovider). Normalmente, es el nombre de la subclase ContentProvider que implementa el proveedor
No hay valor predeterminado. Al menos una autoridad debe ser especificada.
Preguntas
- ¿Es posible que una sola aplicación exponga un
FileProvider
con múltiples autorizaciones y rutas de archivos?- Si es así, ¿qué necesito cambiar para que funcione?
- Si no es así, ¿hay otras formas de configurar el uso compartido de archivos dentro de mi biblioteca que eviten conflictos como este?
Enlaces de apoyo
También me enfrento a este problema y utilizo este enfoque para resolverlo. al igual que tengo un selector de imágenes de biblioteca que utiliza el proveedor de archivos y también mi aplicación usa un proveedor de archivos cuando construyo mi aplicación.
mi archivo proporcionar es
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="org.contentarcadeapps.photoeditor.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"/>
</provider>
cambiar esto a
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="org.contentarcadeapps.photoeditor.fileprovider"
android:exported="false"
android:grantUriPermissions="true"
tools:replace="android:authorities">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"
tools:replace="android:resource"/>
</provider>