背景

我维护了一个library,其核心功能涉及与外部电子邮件应用程序共享以编程方式捕获的屏幕截图。

我使用FileProvider完成此操作,这意味着我的图书馆 list 中包含a <provider> tag:

<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共享文件。我的期望是,如果使用消费的应用程序使用以下 list <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>

此 list 条目:
  • 指定两个Provider授权机构,${applicationId}.fileprovider(用于应用程序文件共享)和${applicationId}.bugshaker.fileprovider(用于库文件共享);
  • 引用更新的filepaths.xml,其中包含应用程序生成的文件和库生成的文件的单独目录定义:

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

    构建应用程序后,我们已经确认生成的 list 已将正确的节点替换为这些更新的值。

    但是,当使用此配置的应用程序组装(成功)并运行时,我们会在启动时看到崩溃:

    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然后返回null,从而导致该NPE。

    如果我在此指令暂停时手动调用resolveContentProvider并传递"${applicationId}.fileprovider""${applicationId}.bugshaker.fileprovider",则resolveContentProvider会返回一个非null的ProviderInfo实例(这似乎是预期的结果)。

    这种差异使我感到困惑,因为 <provider> element documentation指出支持多个权限:



    问题
  • 是否可以让一个应用程序公开具有多个权限和文件路径的FileProvider
  • 如果是这样,我需要进行哪些更改才能使其正常工作?
  • 如果没有,是否还有其他方法可以在我的库中配置文件共享,从而避免诸如此类的冲突?
  • 最佳答案

    我对这个问题的解决方案实际上是避免依赖单个FileProvider解析多个权限。尽管这并不能直接解决上述问题,但我还是将其发布以供后代引用。

    我更新了库,以利用FileProvider的空子类,以便库的更新 list 提供程序条目现在为:

    <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)消耗我的库的应用程序合并 list 现在将包含下面显示的两个条目(不冲突!):

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

    直到同事指出这一点,我才意识到这是一个潜在的解决方案。我以前的假设是(并且错误地)必须将 list 中的所有FileProvider设置为

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

    但是快速检查the documentation发现了我的错误:

    关于android - 可以对FileProvider使用多个权限吗?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/43175014/

    10-11 22:31