Подтвердить что ты не робот

Можно использовать несколько полномочий с помощью FileProvider?

Фон

У меня есть библиотека, основная функция которой заключается в совместном использовании программно снятых снимков экрана с внешними почтовыми приложениями.

Для этого я использую FileProvider, что означает, что мой библиотечный манифест содержит тег <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 определяется следующим образом:

<paths>
    <files-path path="bug-reports/" name="bug-reports" />
</paths>

У пользователя моей библиотеки есть приложение, которое само использует FileProvider для обмена файлами. Я ожидал, что у обоих поставщиков будет возможность обмениваться файлами, если приложение-потребитель использует следующий манифестный тег <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>

Эта запись манифеста:

  • указывает два органа Provider, ${applicationId}.fileprovider (для общего доступа к файлам приложения) и ${applicationId}.bugshaker.fileprovider (для общего доступа к файлам библиотеки);
  • ссылается на обновленный filepaths.xml, который содержит отдельные определения каталогов для файлов, генерируемых приложением, и файлов, генерируемых библиотекой:
<paths>
    <external-path
        name="redacted"
        path="" />
    <files-path
        name="bug-reports"
        path="bug-reports/" />
</paths>

После создания приложения мы подтвердили, что в сгенерированном манифесте были заменены правильные узлы с этими обновленными значениями.

Однако, когда приложение, использующее эту конфигурацию, собрано (успешно) и запущено, мы видим сбой при запуске:

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) 

Используя отладчик, я вижу, что метод FileProvider.parsePathStrategy вызывает PackageManager.resolveContentProvider со строкой полномочий "${applicationId}.fileprovider;${applicationId}.bugshaker.fileprovider". resolveContentProvider затем возвращает ноль, что приводит к этому NPE.

Если я вручную вызову resolveContentProvider во время паузы в этой инструкции и передам либо "${applicationId}.fileprovider", либо "${applicationId}.bugshaker.fileprovider", resolveContentProvider вместо этого вернет ненулевой экземпляр ProviderInfo (который, как представляется, является ожидаемым результатом). Это различие сбивает меня с толку, потому что

документация по элементу <provider> element documentation гласит, что поддерживается несколько прав доступа:

Список одного или нескольких полномочий URI, которые идентифицируют данные, предлагаемые поставщиком контента. Несколько авторитетных источников перечисляются путем разделения их названий точкой с запятой. Чтобы избежать конфликтов, имена авторитетов должны использовать соглашение об именах в стиле Java (например, com.example.provider.cartoonprovider). Как правило, это имя подкласса ContentProvider, которое реализует поставщик

Там нет по умолчанию. Должен быть указан как минимум один орган.

Вопросы

  • Возможно ли, чтобы одно приложение выставляло FileProvider с несколькими правами доступа и путями к файлам?
    • Если да, что мне нужно изменить, чтобы это работало?
    • Если нет, есть ли другие способы настроить общий доступ к файлам в моей библиотеке, чтобы избежать конфликтов, таких как этот?
4b9b3361

Ответ 1

Мое решение этой проблемы на самом деле состояло в том, чтобы не полагаться на один FileProvider разбор нескольких авторитетов. Хотя это не касается непосредственно указанного вопроса, я выкладываю его для будущих поколений.


Я обновил свою библиотеку, чтобы использовать пустой подкласс FileProvider, так что теперь в библиотеке обновлен провайдер манифеста:

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

Объединенный манифест приложения, которое (1) использует запас FileProvider и (2) использует мою библиотеку, теперь будет содержать обе записи, показанные ниже (без коллизий!):

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

Я не осознавал, что это потенциальное решение, пока коллега не указал на это. Мое предположение ранее (и неверно) заключалось в том, что все FileProvider в манифесте должны установить

android:name="android.support.v4.content.FileProvider"

но быстрая проверка документации показала мою ошибку:

Имя класса, который реализует контент-провайдер, подкласс ContentProvider. Это должно быть полное имя класса (например, "com.example.project.TransproductionProvider"). [...]

Ответ 2

Я также сталкиваюсь с этой проблемой и использую этот подход, чтобы решить ее. как будто у меня есть средство выбора изображений библиотеки, которое использует провайдер файлов, а также мое приложение использует провайдер файлов, когда я создаю конфликт между приложениями.

мой файл

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

измените это на

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