1 the use of

  • step1

Add dependencies to build.gradle: Implementation “androidx.startup: startup-Runtime :1.0.0-alpha02”; implementation “androidx.startup: startup-Runtime :1.0.0-alpha02”; implementation “Androidx. startup:startup- Runtime :1.0.0-alpha02”; android.useAndroidX=true

  • step2

Establish an initialization class, need to implement androidx. Startup., Initializer, and realize the inside of the two methods:

public class ToolsInitializer implements Initializer<Boolean> { @NonNull @Override public Boolean create(Context Sharetools.init (context) {return shareTools.init (context); return shareTools.init (context); } @NonNull @Override public List<Class<? extends Initializer<? >>> dependencies() {add BaseInitializer (BaseTools) to List<Class<? extends Initializer<? >>> list = new ArrayList<>(); list.add(BaseInitializer.class); return list; } // This class initializes BaseTools public class BaseInitializer implements Initializer<Boolean> {@nonnull @override public Boolean create(Context context) { BaseTools.init(context); Return true; // Return true; } @NonNull @Override public List<Class<? extends Initializer<? >>> dependencies() {return collections.emptyList (); }}Copy the code
  • step3

Add in the manifest file

<provider android:name="androidx.startup.InitializationProvider" android:authorities="${applicationId}.androidx-startup"  android:exported="false" tools:node="merge"> <meta-data android:name="com.example.androidxtest.ToolsInitializer" /> </provider> </provider>Copy the code

After that, the app starts, will find androidx. Startup. InitializationProvider and obtain meta list, in order to carry out their the create method to initialize, if dependencies returns null, is to carry out their first create methods, Only ToolsInitializer is added to the listing file. However, the BaseInitializer is initialized first because the List returned in dependencies of ToolsInitializer contains the BaseInitializer.

Of course you can:

<provider android:name="androidx.startup.InitializationProvider" android:authorities="${applicationId}.androidx-startup"  android:exported="false" tools:node="merge"> <meta-data android:name="com.example.androidxtest.BaseInitializer" // This is in front, To initialize the android: value = "androidx. Startup" / > < meta - data android: name = "com. Example. Androidxtest. ToolsInitializer" / / the behind, Androidx. startup /> </provider>Copy the code

The result is the same either way

  • Delayed initialization

If you want to delay the execution of an initializer, just

  • 1: Set node=”remove” and delay initialization of ToolsInitializer as follows:
<provider android:name="androidx.startup.InitializationProvider" android:authorities="${applicationId}.androidx-startup"  android:exported="false" tools:node="merge"> <meta-data android:name="com.example.androidxtest.BaseInitializer" android:value="androidx.startup" /> <meta-data android:name="com.example.androidxtest.ToolsInitializer" /> </provider> </provider>Copy the code
  • 2: Executes where initialization is required
AppInitializer.getInstance(this).initializeComponent(ToolsInitializer.class);
Copy the code

If you want all initializers to be delayed, you can simply add node=”remove” to the tag, such as:

<provider android:name="androidx.startup.InitializationProvider" android:authorities="${applicationId}.androidx-startup" Android :exported="false" tools:node="remove"> android:exported="false" tools:node="remove" android:name="com.example.androidxtest.BaseInitializer" android:value="androidx.startup" /> <meta-data android:name="com.example.androidxtest.ToolsInitializer" tools:node="remove" android:value="androidx.startup" /> </provider>Copy the code

Then, as above, execute where needed

AppInitializer.getInstance(this).initializeComponent(xxxx);
Copy the code

Can be

2 Source code Analysis

The ContentProvider’s onCreate() is executed before the Application’s onCreate(), so you can Google it

First we see the manifest file, found that androidx. Startup. InitializationProvider, we look at its source directly, only look at the key part here

public final class InitializationProvider extends ContentProvider { @Override public boolean onCreate() { Context context = getContext(); if (context ! = null) {// Use simple interest mode, and call discoverAndInitialize(), after! AppInitializer.getInstance(context).discoverAndInitialize(); } else { throw new StartupException("Context cannot be null"); } return true; }... }Copy the code

AppInitializer.discoverAndInitialize()

Void discoverAndInitialize() {try {// Get Provider ComponentName Provider = new ComponentName(mContext.getPackageName(),InitializationProvider.class.getName()); / / get the < meta - data > ProviderInfo ProviderInfo = mContext. GetPackageManager (). GetProviderInfo (provider, GET_META_DATA); Bundle metadata = providerInfo.metaData; String startup = McOntext.getstring (r.sing.androidx_startup); // Note the tag, which is "androidx.startup"; If (metadata! = null) { Set<Class<? >> initializing = new HashSet<>(); Set<String> keys = metadata.keySet(); For (String key: keys) {// Get the android:value value in <meta-data> String value = metadata.getString(key, null); If (startup.equals(value)) {// get the Class<? > clazz = Class.forName(key); If (, Initializer. Class. IsAssignableFrom (clazz)) {/ / get the class object class <? extends Initializer<? >> component =(Class<? extends Initializer<? >>) clazz; // doInitialize(Component, initializing); } } } } } catch (PackageManager.NameNotFoundException | ClassNotFoundException exception) { throw new StartupException(exception); } finally { Trace.endSection(); }}Copy the code

“Androidx. startup” = “androidx.startup”; “androidx.startup” = “androidx.startup”;

 <meta-data
    android:name="com.example.androidxtest.BaseInitializer"
    android:value="androidx.startup" />
Copy the code

To the above analysis is the key – value form, the key to “com. Example. Androidxtest. BaseInitializer”, the value of “androidx. Startup”, found that the value is “androidx. Startup”, We will use the key as the className and use reflection to get our BaseInitializer for initialization, so make sure the name is written as the full className

The next see AppInitializer doInitialize

<T> T doInitialize(@NonNull Class<? extends Initializer<? >> component,@NonNull Set<Class<? >> initializing) {synchronized (sLock) {try {// If this class is initializing, If (initializing. Contains (Component)) {String Message = string. format("Cannot initialize % S. Cycle detected.", component.getName() ); throw new IllegalStateException(message); } Object result; // If it has not been initialized, mark it as initializing if (! mInitialized.containsKey(component)) { initializing.add(component); Try {/ / reflection Object creation Object instance = component. GetDeclaredConstructor (). The newInstance (); Initializer<? Initializer<? Initializer<? > initializer = (Initializer<? >) instance; // Get dependencies List<Class<? extends Initializer<? >>> dependencies =initializer.dependencies(); // If the list is not empty, initialize the list first. dependencies.isEmpty()) { for (Class<? extends Initializer<? >> clazz : dependencies) { if (! mInitialized.containsKey(clazz)) { doInitialize(clazz, initializing); }}} // Call the create(Context) method result = initializer.create(mContext); Initializing (component) initializing (component) initializing (component); MInitialized. Put (Component, result); } catch (Throwable throwable) { throw new StartupException(throwable); }} else {// If it is initialized, return the result directly from the cache list result = mInitialized. Get (Component); } return (T) result; } finally { Trace.endSection(); }}}Copy the code

With that said, the static initialization logic is simple. Now let’s look at the dynamic initialization

AppInitializer.getInstance(this).initializeComponent(ToolsInitializer.class); @NonNull @SuppressWarnings("unused") public <T> T initializeComponent(@NonNull Class<? Extends Initializer<T>> Component) {return doInitialize(Component, new HashSet<Class<? > > ()); }Copy the code

DoInitialize () is used to initialize dynamically, but it is not used to initialize dynamically

Tips: node=”remove” When the manifest file is merged, this node is removed, so it is not parsed into the collection and is not initialized