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 a context to initialise, which is not such an easy task to achieve since the first time that 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 Application Object. In this article I’ll show you another way of doing it.
So I guess many of you have seen something like this before:
Why is this? Well the libraries have to do some initialisation to be able to run. Here for example Jake Whartons (btw. great) library ThreeTenABP.
The bad thing on this kind of initialisation is, that even if we wouldn’t need to implement a custom Application, we need to do it. Additionally, we should not forget about this, otherwise we’ll run into crashes.
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 this solution in the firebase-common package, so they’re using it to initialise firebase exactly the same way.
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 onCreate). This means that at this point no activity has been started, but we have access to the (Application)Context, where we can initialise our library using this Context.
Let’s jump right into it and create an empty ContentProvider:
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 Library-Manifest:
And here comes a tricky part. If we would use a fixed authority string, we would only be able to run on one Application per Android Device. This is why you have to make the authorities unique. A good idea 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.
This approach could be a game-changing hint for android libraries, since you can register LifecycleCallbacks in the Application, before any Activity actually started. With this you can reduce the amount of Library-Methods that require an Context’s.
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.
I’ve shown you how to get access to the Application Context from the library Level. Firebase is using this approach internally too. To be honest, it’s not the nicest solution, it’s more a workaround. But it gets the job done.