Auto-initialize your android library

I’ve been writing a couple of android libraries open and closed source ones.
At one point, a library(not all of them, but a few) need an Android Context to initialise,
which is not such an easy task as it may sound, since the very first time the Context is available is in the
onCreate method of the Application Object. That’s why many libraries have an init method, which you have to
call in the Applications onCreate Method. In this article I’ll show you another way of accessing the context before any activity was created.

So I guess many of you have seen something like this before:

1
2
3
4
5
6
7
class YourApplication : Application {
override fun onCreate() {
super.onCreate()
SomeLibrary.init(this)
SomeOtherLibrary.init(this)
}
}

Why do we need this? Well, the libraries have to do some initialisation to be able to run.
Here for example Jake Whartons ThreeTenABP library.

The ugly things on this kind of initialisation are:

  • we need to implement an Application class for initialising a third party library.
  • easy to forget, which might lead to run time crashes.
  • when adding/removing we need to touch code. (Most of the time you have to do this anyways, but in some you don’t)

As library developers we have to think of the developers using our libraries as “Users”.
As we all know “Users” - especially developers - are lazy, when it comes to writing lines of code.

That’s why we want to avoid having those initialisations. I found a solution in the firebase-common package, so they’re using it to initialise firebase as I will describe it.

How does it work?

We create an empty ContentProvider, register it in our manifest and don’t expose it to other apps.

What happens on application start is, that it registers all ContentProviders in the system (calling the Applications onCreate method). This means that at this point no Activity has been started yet, but we have access to the (Application)Context, where we can initialise our library using this Context.

ContentProvider

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class YourLibraryInitProvider : ContentProvider() {
override fun onCreate(): Boolean {
// get the context (Application context)
doSomethingWith(context)
// initialize whatever you need
}

override fun insert(uri: Uri, values: ContentValues?): Uri? = null

override fun query(
uri: Uri,
projection: Array<out String>?,
selection: String?,
selectionArgs: Array<out String>?,
sortOrder: String?) = null

override fun update(uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array<out String>?) = 0

override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?) = 0

override fun getType(uri: Uri): String? = null
}

As you can see in the onCreate Method we’re able to initialise whatever we need to initialise there. Additionally, we have to register the ContentProvider within our libraries Manifest file.

Library-Manifest

1
2
3
4
5
6
7
8
9
10
<manifest package="some.library.packagename"
xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<provider android:authorities="${applicationId}.yourlibraryinitprovider"
android:exported="false"
android:enabled="true"
android:name=".YourLibraryInitProvider" />
</application>

</manifest>

And here comes a very important part. If we would use a fixed authorities string, we would only be able to run on one Application per Android Device. This is why you have to make sure, the authorities are unique. A good practice is, to use the applicationId + some string in this case .yourlibraryinitprovider.

The downside of this is, that if we (for some reason) don’t set the applicationId in our gradle file, the library would
use the one we set in the library. This is why we have to double check if the applicationId is NOT the one we’re using
library internal. We can do this by overriding the attachInfo method of the ContentProvider.

Make it failsafe

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class YourLibraryInitProvider : ContentProvider {
...
override fun attachInfo(context: Context, providerInfo: ProviderInfo?) {
if (providerInfo == null) {
throw new NullPointerException("YourLibraryInitProvider ProviderInfo cannot be null.")
}
// So if the authorities equal the library internal ones, the developer forgot to set his applicationId
if ("<your-library-applicationid>.yourlibraryinitprovider" == providerInfo.authority) {
throw IllegalStateException("Incorrect provider authority in manifest. Most likely due to a "
+ "missing applicationId variable your application\'s build.gradle.")
}
super.attachInfo(context, providerInfo)
}

...
}

Advantages

This approach is very helpful for android libraries, since you can register LifecycleCallbacks in the Application, before any Activity has been started. With this you can reduce the amount of Library-Methods that require an Android Context.

Downsides?!

The onCreate-Method of the ContentProvider will be executed on the “main”-Thread, meaning that multiple providers will take at least the same time for initialising as if you would do it in the Applications onCreate plus the system registration. So you want to initialise them asynchronous (in both cases) if possible. Another fact to think about is, what happens if you introduce multiple libraries with this ContentProvider approach. Since all onCreate’s of all ContentProviders will be called, this also is a bit more time consuming, which is a reason more to do initialisation asynchronous. If your library can be initialised independent of the Applications process (probably not), you can try initialising it in a different process using android:process in the provider tag of the manifest. In most cases you don’t want that.

Update

On the 10th of june 2020, google announced the first version of “startup”, an androidx library that’s supposed to replace the solution provided in this article. Let’s have a closer look.
You are supposed to implement your Initializer like this:

1
2
3
4
5
6
class SomeInitializer : Initializer<SomeContent> {
override fun create(context: Context): SomeContent {
// do some things to provide
return SomeContent(context)
}
}

you have to add it, to the manifest but wait…, that looks familiar to what we’ve seen above.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<manifest package="some.library.packagename"
xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<provider android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<!-- This entry makes SomeInitializer discoverable. -->
<meta-data android:name="com.example.SomeInitializer"
android:value="androidx.startup" />
</provider>
</application>

</manifest>

What does this tell us? Well the “startup” library does more-less the same as the provided solution above, with some adjustments:

  • It merges multiple Initializers together, so that there’s only one Initializer per application. (Remember, in the above’s solution it was one Initializer per library)
    Due to the fact that you’re supposed to use meta-data to provide your Initializer, they’re using this Tag to load all Initializers for a (singular) application.
  • Backgrounding the Initializations in order to avoid blocking ui on startup.

Additionally, it is possible now, to define in which order the initializations are happening, if you have more than one, by implementing the dependencies() method of the Initializer. Details in the startup documentation.

Summary

I’ve shown you how to get access to the Application Context from the library Level. Since google introduced their new “startup” library, there are no downsides left, which were provided or have been discussed here.
Finally a nice and convenient way to initialize your library.