< Back to articles

The App Startup Pitfalls

Jan MottlJan Mottl
September 09, 2022

If you are an Android developer, you probably heard about the App Startup library. It provides a performant way to initialize components at the app startup. A lot of library authors implement an automatic initialization of their libraries at the app startup using content providers. If your app uses a lot of libraries, which use content providers, it can have a significant negative impact on the app startup time. The App Startup library solves this issue by allowing to define component initializers that share a single content provider. Each library developer can implement an initializer instead of the content provider and all libraries can then share one content provider in the app. You can read more details about the App Startup library in this article or official docs.

Apart from using the App Startup as a library developer, you can use it also as an app developer in your app. It can be especially useful for bigger apps that need to initialize a lot of libraries manually in their Application class (i.e. Koin or Timber). By using the App Startup library, you can extract these initializations to separate initializers, making your code cleaner and reducing the amount of code in the Application class or removing it completely. Even though it sounds great, there is one issue with this approach that does not have to be really obvious. This article explains it together with the solution.

The App Startup Library: Problem

App Startup uses a content provider to initialize all components (both from libraries and your app). The problem might come when you also use a library that does not use App Startup yet and uses its own content provider. When you want to use this library in some initializer, it can easily happen that the library was not yet initialized because its content provider was not called yet. It is really easy to fall into this trap, because this situation is new. Without App Startup, all initializations usually happen in the Application class that is called after all content providers are created, so you can safely access all libraries that use their own content provider for initialization. When using App Startup, your initializers accessing libraries’ code are on the same “initialization level” as content providers of those libraries. Here is a picture for a better illustration

schema of initialization flow

So when Initializer 3 accesses a library code initialized in Content provider B, the app will probably crash or will not behave correctly. On the other hand Application can safely and reliably access any code that is initialized in any content provider. As we can see, the real problem here is the order of creation of content providers. If you need one initializer in App Startup to depend on another one, you can declare this dependency to be sure that the initialization order will always be correct. Unfortunately the initializer can’t declare dependency on the content provider. Or at least not directly. Fortunately, there is a way, how you can influence the order of creation of content providers, so let’s see how it works.

Init order of content providers

When you declare a content provider in the manifest, you can also specify the initOrder attribute. It allows the application to express dependencies between content providers and specify the order of their creation. The type of the value is Integer and the higher the value is, the sooner a content provider is created. So for example let’s have these three content providers declared in the manifest together with a specified initOrder:

…  
        ….  
                    …  
            android:name=.ContentProviderA”  
            android:initOrder=-1/>  
                    …  
            android:name=.ContentProviderB”  
            android:initOrder=0/>  
                    …  
            android:name=.ContentProviderC”  
            android:initOrder=1/>

At the app startup, the init order of content providers will be: Content provider C ->  Content provider B -> Content providerA. But what if we do not specify the initOrder? What will be the value? This is not mentioned in the docs, but I tested this and as you probably guess, the value will default to 0 (surprise). More interesting question is: What will be the init order of content providers that have the same initOrder value?. The answer is: Random, but not really. Confusing, right? Let’s explore this situation in more detail.

If more content providers have the same initOrder value, the system can’t determine the init order based on this value and thus it selects some order on its own. This order can be both deterministic and nondeterministic depending on the execution context. More specifically, if you run the app with three content providers, which use the same initOrder value, multiple times on one Android version, the init order will always be the same. However, when you run this app multiple times on another Android version, the init order will always be the same on this Android version, but might differ from the previous one. Let’s look at the example that comes from my own testing of this behavior.

table for Init order of content providers on various devices

In the above table, we can see six different content providers (including InitializationProvider from the App Startup) and three different devices running different Android versions. Each cell with the number represents the init order of the particular content provider on the particular device when run on one of our apps. Running the app on API 30 and 31 had the same init order of content providers, but on API 28 the order was different. It does not matter how many times you run the app on the particular device, the order is always the same for it.

Solution

The solution to the init order problem is simple. We need to be sure that our InitializationProvider will be executed as the last one, so we can be sure that our initializers can safely access all code initialized by other content providers. In order to achieve this, we need to set the initOrder value of our InitializationProvider to be smaller than the initOrder value of all other content providers. Usually most of the third party content providers do not have the initOrder specified at all (defaults to 0). In our example only FirebaseInitProvider sets the initOrder value to 100. It means that if we want to be almost 100% sure that our content provider will run as the last one, we can set it to Int.MIN_VALUE (-2147483648). This way only a content provider with the same initOrder value could be executed later on some Android versions and it is very unlikely that any library will set this initOrder value.

Conclusion

App Startup is a great library for improving app startup performance. It can be also used for transforming your big init code in the Application class to smaller focused initializers. However, this can lead to the init order issue and if you want to use this reliably, you should specify the smallest possible initOrder value of your InitializationProvider to avoid it. Alternatively, it could also be a good idea to just leave your custom init code in the Application class or leave at least the code that depends on other libraries there.

Jan Mottl
Jan Mottl
Android Tech LeadHonza is a tech lead for Android team. Besides improving architecture of projects, he likes listening to progressive metal or watching snooker.

Are you interested in working together? Let’s discuss it in person!

Get in touch >