Possible d'utiliser de multiples autorités FileProvider?
Fond
Je maintenir un bibliothèque dont la fonctionnalité principale est le partage de la par programme-captures d'écran à l'externe des applications de messagerie électronique.
- Je utiliser un FileProvider
pour ce faire, ce qui signifie que ma bibliothèque est manifeste contient un <fournisseur>
de la balise:
<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éfinie comme suit:
<paths>
<files-path path="bug-reports/" name="bug-reports" />
</paths>
Un consommateur de ma bibliothèque dispose d'une application qui utilise elle-même un FileProvider
pour partager des fichiers. Mon espoir était qu'il devrait être possible de permettre à la fois aux fournisseurs de partager des fichiers si la demande de consommation utilisé le manifeste suivantes <provider>
tag:
<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>
Ce manifeste entrée:
- spécifie deux
Provider
autorités,${applicationId}.fileprovider
(pour l'application de partage de fichiers) et${applicationId}.bugshaker.fileprovider
(pour la bibliothèque de partage de fichiers); - fait référence à une mise à jour
filepaths.xml
, qui contient le répertoire distinct des définitions pour l'application des fichiers générés et de la bibliothèque des fichiers générés:
<paths>
<external-path
name="redacted"
path="" />
<files-path
name="bug-reports"
path="bug-reports/" />
</paths>
Après la construction de l'application, nous avons confirmé que le manifeste a eu la bonne nœuds remplacé avec ces valeurs mises à jour.
Toutefois, lorsque l'application en utilisant cette configuration est assemblé (avec succès) et de l'exécution, nous voyons un crash lors du 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)
De l'utilisation du débogueur, je suis capable de voir que la méthode FileProvider.parsePathStrategy
appelle PackageManager.resolveContentProvider
avec l'autorité de la chaîne de "${applicationId}.fileprovider;${applicationId}.bugshaker.fileprovider"
. resolveContentProvider
puis renvoie la valeur null, conduisant à ce NPE.
Si j'manuellement appel resolveContentProvider
en pause à cette instruction, et passer "${applicationId}.fileprovider"
ou "${applicationId}.bugshaker.fileprovider"
, resolveContentProvider
au lieu de cela renvoie une valeur non nulle ProviderInfo
instance (qui semble être le résultat attendu).
Cette différence me confond, car la <fournisseur>
de l'élément de documentation stipule que plusieurs autorités sont pris en charge:
Une liste d'un ou plusieurs URI autorités à identifier les données offertes par le fournisseur de contenu. Plusieurs autorités sont répertoriés par la séparation de leurs noms par un point-virgule. Pour éviter les conflits, l'autorité de noms à utiliser un Java-style de la convention d'affectation de noms (comme com.exemple.fournisseur de.cartoonprovider). Généralement, c'est le nom de la ContentProvider sous-classe qui implémente le fournisseur
Il n'y a pas de valeur par défaut. Au moins une autorité doit être spécifié.
Questions
- Est-il possible d'avoir une seule application exposer un
FileProvider
avec de multiples autorités et les chemins d'accès aux fichiers?- Si oui, que dois-je modifier pour faire ce travail?
- Si non, existe-il d'autres façons de configurer le partage de fichiers au sein de ma bibliothèque que d'éviter les conflits comme celui-ci?
L'Appui Des Liens
- "Je suis capable de voir que la méthode PackageItemInfo.loadXmlMetaData est invoqué avec l'autorité chaîne "${identificateur applicationid}.fileprovider;${identificateur applicationid}.bugshaker.fileprovider" -- vous ne fournissez pas de l'autorité de la chaîne de
loadXmlMetaData()
, et je ne vois pas que, dans leFileProvider
code source. Une autorité est fourni àresolveContentProvider()
sur la ligne précédente. C'est ce que tu veux dire? Si oui,ProviderInfo
est de fournir le point-virgule-liste délimitée par des virgules, etFileProvider
ne semble pas gérer ça. - Au-delà, en regardant le code dans
FileProvider
, il semblerait qu'ils ne sont pas de la manipulation de multiples autorité scénario. Ils ont des crochets pour avoir de chemin d'accès multiples stratégies de l'autorité, mais ils ne semblent jamais à analyser le point-virgule-liste délimitée par des virgules. Probablement pas été testé. Je code en monStreamProvider
qui ne parse la liste, mais je ne l'ai pas testé non plus. 🙁 - Ok, mis à jour. Il semble étrange que
parsePathStrategy
récupère une nouvelleProviderInfo
exemple à tous lorsque l'on est à laattachInfo
méthode qui l'appelle. Je vois queContentProvider
ne séparent les autoritésattachInfo
et queFileProvider
appels de super, mais les multiples autorités champ à l'intérieur deContentProvider
ne semble pas être accessible à des sous-classes à tous. - Peut-être une question de base, mais ce n'des applications qui nécessitent différents types de fournisseurs de contenu normalement dans le manifeste? Sont-ils nécessaires pour créer une "base" fournisseur de contenu et le délégué de manière appropriée?
- Dernier commentaire pour la nuit; je vais tester
StreamProvider
dans mon exemple d'application au début de cette semaine et de faire un rapport... - "ce n'des applications qui nécessitent différents types de fournisseurs de contenu normalement dans le manifeste?" - habituellement, ils sont tout à fait différentes classes. Quelques bibliothèques livrées avec un
ContentProvider
; encore moins peut être nécessaire dans le béton (c'est à dire, inscrite dans le manifeste) par 2+ bibliothèques ou 1 autre de la bibliothèque et de l'application elle-même. Donc, je ne pense pas qu'il y est une "normalement". Dans ce cas,FileProvider
(etStreamProvider
) s'appuient sur unstatic
cache de données pour gérer plusieurs autorités, et donc, juste à l'aide d'un simple sous-classe deFileProvider
/StreamProvider
serait insuffisant. - Lors de l'utilisation de la
StreamProvider
je reçois toujours un crash lors du lancement:Attempt to read from field 'android.os.Bundle android.content.pm.PackageItemInfo.metaData' on a null object reference
. Des sons similaires. - De garder un oeil sur ce problème.
Vous devez vous connecter pour publier un commentaire.
Ma solution à ce problème a été effectivement pour éviter de compter sur une seule
FileProvider
l'analyse de plusieurs autorités. Tout cela ne veut pas répondre directement à la question, comme indiqué, je poste pour la postérité.J'ai mis à jour ma bibliothèque tirer parti d'un vide sous-classe de
FileProvider
, de sorte que la bibliothèque manifeste mis à jour le fournisseur de l'entrée est maintenant:La fusion du manifeste de l'application (1) utilise un stock
FileProvider
et (2) la consommation de ma bibliothèque va maintenant contenir à la fois des entrées affichées ci-dessous (pas de collision!):Je ne savais pas que c'était une solution possible jusqu'à ce qu'un collègue l'a signalé. Mon hypothèse avait déjà (et à tort) que tous les
FileProvider
s dans le manifeste doit définirmais une vérification rapide de la documentation révélé mon erreur:
getUriForFile(Context, "com.my.authority.fileprovider", file);
? L'utilisez-vous dans votre bibliothèque, si oui, comment êtes-vous générer dynamiquement l'autorité de chaîne à partir de la deuxième paramètre dans.getUriForFile()
? Je suis à l'aide de laFileProvider
seul dans mon projet de bibliothèque donc, j'ai l'autorité de la chaîne de caractères définis de manière statiquegetUriForFile(Context, "com.my.authority.fileprovider", file);
mais il semble que je pourrais avoir besoin pour générer de façon dynamique.bugshaker
module = bibliothèque,example
module = application. Ma bibliothèque fournisseur est utilisé uniquement à l'intérieur de la bibliothèque, et pas par les applications qui consomment beaucoup d' - on dirait que c'est différent de votre installation?getUriForFile(context, applicationContext.getPackageName() + LIB_SUFFIX, file);
j'ai aussi face à ce problème et d'utiliser cette approche pour résoudre le problème.
comme j'ai une bibliothèque sélecteur d'images qui utilisent fournisseur de fichier et aussi mon app utiliser un fournisseur de fichier lorsque je créer mon application conflit se passe.
mon dossier à fournir est
de changer cela pour