FileProvider and Libraries
TL;DR: If you write an Android library, and it needs FileProvider
capabilities,
create and use your own subclass of FileProvider
, so you can have a separate
<provider>
element in the manifest and not collide with that of the app
or any other library.
A few weeks ago, I blogged about how
libraries need to coordinate JobScheduler
job IDs with their hosting apps,
since those are app-wide constants.
In general, anything that is app-wide or device-wide needs to be configurable by the app developer, even if the library might offer a default solution that works most of the time.
A classic example of this is Application
. An app can only have one
Application
class registered on the <application>
element. For a while,
too many libraries were insisting that, to use the library, you had to use
their Application
class (or extend from it). Narrowly, that’s fine, but once
the app developer needs 2+ libraries with this requirement, the developer is
screwed, as Java does not support multiple inheritance. Instead, libraries
might offer a canned Application
class, but need to support developers
initializing the library by some other means that does not require a magic
Application
class.
The job ID scenario that I blogged about is another example: while the library might default to certain job IDs, those values need to be configurable, in case some other library also defaults to those IDs. The app developer needs to be able to mediate the conflict.
Another example is FileProvider
.
Content providers have two settings that have uniqueness requirements:
-
There can be only one
<provider>
element in a manifest for a givenandroid:name
attribute, pointing to some specific Java class -
The authority strings must be unique for a device
Historically, this was not a big problem. Few libraries needed to have a
<provider>
element, and many of those that did needed it because the
point of the library was to provide the provider.
FileProvider
, though, is a bit different. in that libraries might need it to
overcome the ban on the file
scheme
instituted in Android 7.0.
If a library has a <provider>
element for android.support.v4.content.FileProvider
,
now neither the app nor libraries can have that element. In principle, they could
and let the manifest merger somehow sort it out, but that does not work well
for authority strings or the FileProvider
required <meta-data>
element.
However, as Stuart Kent points out, you can create your own trivial subclass of FileProvider
:
public class MyVeryOwnFileProvider extends FileProvider {
}
Now, you can have a <provider>
element that points to your custom
FileProvider
subclass, and (usually) it will not conflict with that of anyone
else. Your authority string and <meta-data>
can live alongside that of others
in the app, and FileProvider
handles this situation, even for things like
FileProvider.getUriForFile()
.
However, your authority string still needs to be unique on the device. Base it off
of the ${applicationId}
and have a nice distinctive suffix that prevents
accidental collision (e.g., android:authorities="${applicationId}.your.library.package.name.goes.here.provider")
).
Now, once again, you should be independent from other libraries or from the
app’s own use of FileProvider
.
In many respects, these are all just variants on library-level encapsulation. You don’t want the implementation of the library to “leak” and pollute namespaces outside of the library’s control. It’s just that Android make library-level encapsulation trickier than you may encounter in other platforms.