Contexte
Je maintiens un bibliothèque dont la fonctionnalité principale consiste à partager des captures d'écran capturées par programme avec des applications de messagerie électronique externes.
J'utilise un FileProvider
pour accomplir cela, ce qui signifie que le manifeste de ma bibliothèque contient a <provider>
étiquette :
<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
est défini comme suit :
<paths>
<files-path path="bug-reports/" name="bug-reports" />
</paths>
Un consommateur de ma bibliothèque possède une application qui utilise elle-même un fichier FileProvider
pour partager des fichiers. Je m'attendais à ce qu'il soit possible d'autoriser les deux fournisseurs à partager des fichiers si l'application consommatrice utilisait le manifeste suivant <provider>
étiquette :
<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>
Cette entrée manifeste :
- spécifie deux
Provider
autorités,${applicationId}.fileprovider
(pour le partage de fichiers d'application) et${applicationId}.bugshaker.fileprovider
(pour le partage des fichiers de la bibliothèque) ; -
fait référence à une mise à jour
filepaths.xml
qui contient des définitions de répertoire distinctes pour les fichiers générés par l'application et les fichiers générés par la bibliothèque :<paths> <external-path name="redacted" path="" /> <files-path name="bug-reports" path="bug-reports/" /> </paths>
Après avoir construit l'application, nous avons confirmé que les nœuds corrects du manifeste généré ont été remplacés par ces valeurs actualisées.
Cependant, lorsque l'application utilisant cette configuration est assemblée (avec succès) et exécutée, nous constatons un crash au lancement :
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)
En utilisant le débogueur, je suis capable de voir que la méthode FileProvider.parsePathStrategy
invoque PackageManager.resolveContentProvider
avec la chaîne d'autorité "${applicationId}.fileprovider;${applicationId}.bugshaker.fileprovider"
. resolveContentProvider
retourne alors null, ce qui conduit à ce NPE.
Si j'appelle manuellement resolveContentProvider
tout en faisant une pause à cette instruction et passer soit "${applicationId}.fileprovider"
o "${applicationId}.bugshaker.fileprovider"
, resolveContentProvider
renvoie plutôt un ProviderInfo
(ce qui semble être le résultat attendu).
Cette différence me perturbe car le <provider>
documentation des éléments indique que les autorités multiples sont supportées :
Une liste d'une ou plusieurs autorités URI qui identifient les données offertes par le fournisseur de contenu. Les autorités multiples sont listées en séparant leurs noms par un point-virgule. Pour éviter les conflits, les noms des autorités doivent utiliser une convention d'appellation de style Java (comme com.example.provider.cartoonprovider). En général, il s'agit du nom de la sous-classe ContentProvider qui met en œuvre le fournisseur.
Il n'y a pas de valeur par défaut. Au moins une autorité doit être spécifiée.
Questions
- Est-il possible d'avoir une application unique qui expose une
FileProvider
avec plusieurs autorités et chemins de fichiers ?- Si oui, que dois-je changer pour que cela fonctionne ?
- Si ce n'est pas le cas, existe-t-il d'autres moyens de configurer le partage de fichiers au sein de ma bibliothèque qui permettent d'éviter des conflits tels que celui-ci ?
0 votes
"Je suis en mesure de voir que la méthode PackageItemInfo.loadXmlMetaData est invoquée avec la chaîne d'autorité "${applicationId}.fileprovider;${applicationId}.bugshaker.fileprovider" -- vous ne fournissez pas de chaîne d'autorité pour
loadXmlMetaData()
et je ne vois pas ça dans leFileProvider
le code source. Une autorité est fournie pourresolveContentProvider()
sur la ligne précédente. C'est ce que vous voulez dire ? Si c'est le cas,ProviderInfo
fournit la liste délimitée par des points-virgules, etFileProvider
ne semble pas gérer ça.0 votes
Au-delà de ça, en regardant le code dans
FileProvider
Il semblerait qu'ils ne gèrent pas le scénario des autorités multiples. Ils ont des crochets pour avoir plusieurs stratégies de chemin par autorité, mais ils ne semblent jamais analyser la liste délimitée par des points-virgules. Probablement non testé. J'ai du code dans monStreamProvider
qui analyse la liste, mais je ne l'ai pas testé non plus :-(0 votes
Ok, mise à jour. Il semble étrange que
parsePathStrategy
récupère un nouveauProviderInfo
du tout lorsqu'une instance est fournie à lattachInfo
qui l'appelle. Je vois queContentProvider
ne divise les autorités enattachInfo
et queFileProvider
appelle super, mais le champ "Autorités multiples" dans le champContentProvider
ne semble pas du tout être accessible aux sous-classes.0 votes
C'est peut-être une question basique, mais que font normalement les applications qui nécessitent différents types de fournisseurs de contenu dans le manifeste ? Doivent-elles créer un fournisseur de contenu "de base" et le déléguer de manière appropriée ?
0 votes
Dernier commentaire pour cette nuit ; je vais tester le
StreamProvider
dans mon application modèle en début de semaine et je vous ferai un rapport...0 votes
"que font normalement les applications qui nécessitent différents types de fournisseurs de contenu dans le manifeste ?" -- généralement, ce sont des classes entièrement différentes. Peu de bibliothèques sont livrées avec un
ContentProvider
et moins encore pourraient être nécessaires concrètement (c'est-à-dire enregistrés sous forme de manifeste) par plus de deux bibliothèques ou une autre bibliothèque et l'application elle-même. Par conséquent, je ne pense pas qu'il y ait est a "normalement". Dans ce cas,FileProvider
(yStreamProvider
) s'appuient sur unstatic
de données pour gérer de multiples autorités, et donc l'utilisation d'une simple sous-classe deFileProvider
/StreamProvider
serait insuffisante.0 votes
Lorsque vous utilisez le
StreamProvider
J'ai toujours un crash au lancement :Attempt to read from field 'android.os.Bundle android.content.pm.PackageItemInfo.metaData' on a null object reference
. Sons similaires.1 votes
Gardez un œil sur cette question .