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