library を維持します。その中核機能は、プログラムでキャプチャされたスクリーンショットを外部の電子メールアプリケーションと共有することです。
これを実現するためにFileProvider
を使用します。つまり、ライブラリのマニフェストには a <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
はnullを返し、このNPEにつながります。
この命令で一時停止中に手動でresolveContentProvider
を呼び出し、"${applicationId}.fileprovider"
or"${applicationId}.bugshaker.fileprovider"
、resolveContentProvider
は代わりに、null以外のProviderInfo
インスタンスを返します(これは予想される結果のようです)。
<provider>
要素ドキュメント は複数の権限がサポートされていることを示しているため、この違いは私を混乱させます:
コンテンツプロバイダーが提供するデータを識別する1つ以上のURI機関のリスト。複数の機関は、名前をセミコロンで区切ってリストされます。競合を避けるために、機関名はJavaスタイルの命名規則(com.example.provider.cartoonproviderなど)を使用する必要があります。通常、プロバイダーを実装するContentProviderサブクラスの名前です
デフォルトはありません。少なくとも1つの権限を指定する必要があります。
FileProvider
を公開することは可能ですか?この問題に対する私の解決策は、実際には、複数の権限を解析する単一の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
sを設定する必要があるということでした(そして誤って)
Android:name="Android.support.v4.content.FileProvider"
しかし、 ドキュメント の簡単なチェックは私のエラーを明らかにしました:
ContentProviderのサブクラスであるコンテンツプロバイダーを実装するクラスの名前。これは完全修飾クラス名(「com.example.project.TransportationProvider」など)である必要があります。 [...]
私もこの問題に直面し、このアプローチを使用して解決します。ファイルプロバイダーを使用するライブラリイメージピッカーがあり、アプリの競合が発生したときにアプリがファイルプロバイダーを使用するようにします。
私のファイルは
<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>