Library Authors: You Can't Always Get What You Want
Given the rise of Android Studio, more and more Android library authors are creating Android library projects and publishing them as AARs, as opposed to trying to stick to plain JARs. Nowadays, this approach as the added benefit offering manifest merging, so a library author can contribute elements to the manifest:
-
<uses-permission>
and<uses-feature>
elements that the library needs for various operations -
components (e.g.,
<activity>
) elements, to save the developer using the library to have to manually add them -
a
<uses-sdk>
element, with the library author’s own take on what theminSdkVersion
andtargetSdkVersion
should be -
and so on
Library authors should not get complacent, though, and assume that just because they ask for it in the library’s manifest, that they will get it. Developers have the ability to reject anything introduced by the library’s manifest, through two major approaches:
-
Add
tools:node="remove"
attributes to copies of elements to be blocked, such as havingtools:node="remove"
on a<uses-permission>
element to specifically block libraries from adding that permission requirement -
Add
tools:overrideLibrary
to the<uses-sdk>
element, indicating libraries whose opinions onminSdkVersion
andtargetSdkVersion
should be ignored
Library authors need to practice defensive programming and make sure that they react to the environment around them.
If you gotta gotta gotta have some permission, make sure that your
library “fails fast”, so that the developer is guaranteed to find out
that blocking the permission is not supported. This may come about
naturally by use of your library, such as a camera library failing
quickly when trying to use android.hardware.Camera
if the
CAMERA
permission were blocked. However, if the permission is essential
but may not be used until later in the workflow, use PackageManager
to determine if you have the permission, and fail early on with your
own exception, rather than wait.
If the permission is not essential for the overall library, but it is
essential for some feature of the library, try to gracefully degrade
if the feature is used but the permission is blocked. For example,
some app developers try to avoid WRITE_EXTERNAL_STORAGE
for fear of
backlash from users. If your app would default to using external storage
for something (and therefore perhaps needs this permission), see if
there is a workaround (e.g., have the file on internal storage and use
FileProvider
to serve it to third-party apps). Or, simply fail the
request, ideally in some way that allows the user to understand what
is going on or for the app developer to react to the missing feature.
So, if you need WRITE_EXTERNAL_STORAGE
and do not have it, through
some custom checked exception, or have a canUseXXXFeature()
method
that the app developer should call to determine if the feature can be
used, or something.
The same holds true for anything else that you introduce in the manifest:
-
If you specify a
minSdkVersion
, recognize that your vote may be ignored, so you may be running on an older device. Where possible, try to handle that gracefully (e.g., useBuild.VERSION.SDK_INT
to see what you really are running on and react). Or, check the version information up front and fail fast, or at least introduce a warning to alert the developer that you’re not responsible for anyVerifyError
crashes due to your running on an older-than-expected Android device. -
If you specify a
targetSdkVersion
, you should be able to find out what the actualtargetSdkVersion
is from theApplicationInfo
object that you can get fromPackageManager
. -
If you added components and planned on trying to use those components yourself (e.g., register a
PendingIntent
pointing to aBroadcastReceiver
that you had set up in your manifest), you can usePackageManager
to determine if you actually have thatBroadcastReceiver
registered in the manifest. -
And so on.
Of course, this is all a pain. I need to do a better job of this myself for my CWAC libraries.
:: adds item to to-do list::
On the other hand, we really should have been doing this sort of thing before manifest merging came about, as we were expecting the app developer to manually set up these things in their app’s manifest… and they might not. Just because we can automate putting our requirements in the manifest does not eliminate our need to validate what we got and handle differences appropriately. As some old British guys have pointed out for years, you can’t always get what you want.