As an app or library developer, we may need to initialize the components at the app startup. One solution is initializing your components inside the application class one after the other. Another smart solution that libraries Work Manager or Firebase are using is to define content providers but below are the problems with the Content Provider approach:
1. We need to initialize separate content providers for each component, suppose we have 3 components to initialize then it requires 3 different content providers. Basically, content providers are expensive to instantiate so keeping multiple providers will slow down the startup time.
2. Even if we keep multiple providers Android initializes these in an undetermined order, so any dependencies from one to another will not work.
To overcome the above problems Android provides the Jetpack App Startup library. This library will allow us to designate a single content provider for all initializers in an application and provide dependency to initialize in sequence. This will improve app startup time significantly.
Please follow the below steps to implement the App Startup library.
Step 1:
Add the following to the Gradle file to use this library in our app
dependencies {
implementation "androidx.startup:startup-runtime:1.1.1"
}
Step 2:
Implement component initializers for each component that the app needs to initialize. For that we need to implement the Initializer<T> interface, this interface defines two important methods.
- The create() method performs all the operations required to initialize the component and returns an instance of T.
- The Dependencies() method returns a list of other Initializer objects to which the initializer is dependent. It is possible to control the order in which initializers are run at startup using this method.
For example, if we have a Logger library that needs to be loaded first to track the application flow and write logs to the file. Then define a LoggerInitializer class that implements Initializer<WorkManager>:
// Initializes Logger.
class LoggerInitializer implements Initializer<Logger> {
@Override
public Logger create(Context context) {
Logger.loadLibs(mContext);
return Logger.getInstace(context);
}
@Override
public List<Class<Initializer<?>>> dependencies() {
// No dependencies on other libraries.
return emptyList();
}
}
In onCreate(), we are loading the Logger library and returning the Logger instance. This initializer doesn’t depend on any component so dependencies() is returning an empty list.
Suppose, if our app is using the SQL Cipher library to encrypt the Database and it should be loaded at the start of the application. Then define an SQLCipherDBInitializer class that implements Initializer<WorkManager>:
// Initializes Cipher DB.
class SQLCipherDBInitializer implements Initializer<SqlCipherDB> {
@Override
public SqlCipherDB create(Context context) {
SqlChipherDB lObjDB;
try{
lObjDB = SQLiteDatabase.loadLibs(mContext);
Logger.info(Succefully intialized);
}catch(Exception e){
Logger.error(e.getMessage());
}
return lObjDB;
}
@Override
public List<Class<Initializer<?>>> dependencies() {
// Defines a dependency on LoggerInitializer
return Arrays.asList(LoggerInitializer.class);
}
}
This SQLCipherDBInitializer is dependent on LoggerInitializer to write the logs whether the DB is initialized successfully or not. So including LoggerInitializer in the dependencies() will initialize Logger before the DB initialization.
Step 3:
This library also provides two different approaches:
Auto Initialization
Lazy/manual initialization
Auto Initialization:
This will Initialize all components at the start of the app. So the Application class will get called once all of the components are initialized. This requires registering the InitializationProvider in manifest as below.
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<!-- This entry makes SQLCipherDBInitializer discoverable. -->
<meta-data android:name="com.startup.SQLCipherDBInitializer "
android:value="androidx.startup" />
</provider>
You don’t need to add a <meta-data> entry for LoggerInitializer, because that is a dependency of SQLCipherDBInitializer. This means that if SQLCipherDBInitializer is discoverable, then so is LoggerInitializer.
This tools:node=”merge” will help to merge all InitializationProvider components registered in different manifest files in the project.
Lazy/manual initialization:
We can use AppInitializer to manually initialize that component and its dependencies. Not all the components are required to initialize at app startup so this API will help to initialize the component whenever we want, that is Lazy initialization.
AppInitializer.getInstance(context)
.initializeComponent(SQLCipherDBInitializer.class);
We should remove the auto initialization before using the manual. So we can completely remove the InitializationProvider and initialize all components manually.
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
tools:node="remove" />
The above code snippet will remove the InitializationProvider completely.
Or we can remove a particular component as well and initialize that manually.
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<meta-data android:name="com.example.SQLCipherDBInitializer "
tools:node="remove" />
</provider>
In the above code snippet, only SQLCipherDBInitializer will be removed from auto initialization and later we can initialize it where ever needed.