Startup Startup

Source code version:

  • Startup: 1.1.0

Navigation:

  • Jetpack-lifecycle source code parsing
  • Jetpack-livedata source code parsing
  • Jetpack-viewmodel source code
  • Jetpack-startup
  • See more articles here: home page

use

Implementation, Initializer

class WorkManagerInitializer : Initializer<WorkManager> {
    
    override fun create(context: Context): WorkManager {
        // Initialize the WorkManager
        val configuration = Configuration.Builder().build()
        WorkManager.initialize(context, configuration)
        // Return the initialized instance of WorkManager, So that through AppInitializer. GetInstance (context). InitializeComponent (WorkManagerInitializer: : class. Java) initialization and obtain.
        return WorkManager.getInstance(context)
    }
    
    override fun dependencies(a): List<Class<out Initializer<*>>> {
        // No dependencies on other libraries
        return emptyList()
    }
}
Copy the code

Because WorkManager does not depend on any other libraries, the dependencies() method returns an empty list.

class ExampleLoggerInitializer : Initializer<ExampleLogger> {
    override fun create(context: Context): ExampleLogger {
        // Initialize ExampleLogger and return its object, So that through AppInitializer. GetInstance (context). InitializeComponent (ExampleLoggerInitializer: : class. Java) initialization and obtain.
        return ExampleLogger(WorkManager.getInstance(context))
    }

    override fun dependencies(a): List<Class<out Initializer<*>>> {
        / / rely on WorkManagerInitializer
        return listOf(WorkManagerInitializer::class.java)
    }
}
Copy the code

Because ExampleLogger depends on WorkManager, the dependencies() method returns a list containing WorkManagerInitializer that is initialized before it.

Initialization, Initializer

Automatically initialize components

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.ExampleLoggerInitializer"// to initializeInitializerClass name full pathandroid:value="androidx.startup" />
</provider>
Copy the code

Automatically initialize the component, which initializes the component when the application starts. There is no need to add a

entry for WorkManagerInitializer because it is a dependency on ExampleLoggerInitializer. This is not a problem.

Description:

  1. theprovidertheDeclaration format fixed, only need to modify<meta-data>thenameCan.
  2. the<meta-data>theThe orderAnd determine theThe order of initialization.
  3. thetools:node="merge"Property to ensure list merge toolProperly resolve any conflicting entries.

Manually initialize components

Manually initializing a component means not automatically initializing the component, so you must disable automatic initialization, also known as lazy initialization.

Disable automatic initialization of a single component

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

To disable automatic initialization for a single component, add tools:node=”remove” to the specified

.

Description:

  1. if<meta-data>Own (Can be modified), delete the specified<meta-data>Can; if<meta-data>Not your own (e.g., tripartite libraries,Do not modify the), is usedtools:node="remove"Just declare it.
  2. Disabling automatic initialization of a component also disables automatic initialization of that component’s dependencies.
  3. tools:node="remove", fromThe combined listingIn theremoveThis element.

Disable automatic initialization for all components

<provider
    android:name="androidx.startup.InitializationProvider"
    android:authorities="${applicationId}.androidx-startup"
    tools:node="remove" />
Copy the code

To disable automatic initialization for all components, declare tools:node=”remove” on InitializationProvider’s provider. Leave everything else unchanged or delete all

.

Description:

  1. Not recommendedAll of the componentsIs automatically initialized because it willDisable allusestartuplibraryInitialize thetheThree party libraries(e.g.,lifecycleThe libraryProcessLifecycleInitializer) give rise to the needmanualInitialize theallusestartupTripartite library library, not convenient for follow-up maintenance.

Manually initialize components

val result = AppInitializer.getInstance(context)
    .initializeComponent(ExampleLoggerInitializer::class.java)
Copy the code

Manual initialization component, called AppInitializer. InitializeComponent () method, the above code manual initialization ExampleLogger, because ExampleLogger depend on WorkManager again, So the WorkManager is also initialized.

Description:

  1. The return valueresultforExampleLoggerInitializerthecreate()Method returnExampleLoggerInstance.
  2. ifAppInitializer.initializeComponent()Method initialized byInitializerIt is already initialized. A call to it again will not be initialized again, but will returnInitialization for the first timetheThe resulting value(such as initializationExampleLoggerInitializer, always returnsInitialization for the first timetheExampleLoggerInstance).

The source code

Implementation, Initializer

, Initializer class

public interface Initializer<T> {

    // Initialize the component given the application context
    @NonNull
    T create(@NonNull Context context);

    // The Initializer dependency list. This is used to determine the initialization order of initializers.
    For example, if an initializer B defines another initializer A as its dependency, then A is initialized before B.
    @NonNullList<Class<? extends Initializer<? >>> dependencies(); }Copy the code

The Initializer class, for the Initializer interface, defines two important methods:

  • create()Method, which containsAll actions required to initialize the componentAnd returnT an instance of the.
  • dependencies()Method, which returnsInitializerDepends on othersInitializer<T>The object’sThe list of. You can use this methodcontrolappinAt the startruninitializerstheThe order.

Initialization, Initializer

Automatically initialize components

Let’s take a look at the startup library manifest

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="androidx.startup" >

    <uses-sdk
        android:minSdkVersion="14"
        android:targetSdkVersion="30" />

    <application>
        <provider
            android:name="androidx.startup.InitializationProvider"
            android:authorities="${applicationId}.androidx-startup"
            android:exported="false"
            tools:node="merge" />
    </application>

</manifest>
Copy the code

It declares the minimum SDK version 14 and declares a ContentProvider, which is InitializationProvider. Let’s look at the InitializationProvider class.

InitializationProvider class

public class InitializationProvider extends ContentProvider {
    @Override
    public final boolean onCreate(a) {
        Context context = getContext();
        if(context ! =null) {
            AppInitializer.getInstance(context).discoverAndInitialize();
        } else {
            throw new StartupException("Context cannot be null");
        }
        return true;
    }

    @Nullable
    @Override
    public final Cursor query(
            @NonNull Uri uri,
            @Nullable String[] projection,
            @Nullable String selection,
            @Nullable String[] selectionArgs,
            @Nullable String sortOrder) {
        throw new IllegalStateException("Not allowed.");
    }

    @Nullable
    @Override
    public final String getType(@NonNull Uri uri) {
        throw new IllegalStateException("Not allowed.");
    }

    @Nullable
    @Override
    public final Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        throw new IllegalStateException("Not allowed.");
    }

    @Override
    public final int delete(
            @NonNull Uri uri,
            @Nullable String selection,
            @Nullable String[] selectionArgs) {
        throw new IllegalStateException("Not allowed.");
    }

    @Override
    public final int update(
            @NonNull Uri uri,
            @Nullable ContentValues values,
            @Nullable String selection,
            @Nullable String[] selectionArgs) {
        throw new IllegalStateException("Not allowed."); }}Copy the code

InitializationProvider class, which is a ContentProvider, In the onCreate () method calls inside the AppInitializer. GetInstance (context). DiscoverAndInitialize (), . Let’s take a look at AppInitializer getInstance (context) method, and then take a look at its discoverAndInitialize () method.

Description:

  1. Application,ContentProvider,ActivitytheonCreate()Order of execution:Application.attachBaseContext() -> ContentProvider.onCreate() -> Application.onCreate() -> Activity.onCreate().

AppInitializer –> getInstance method

public final class AppInitializer {

    // A singleton AppInitializer instance
    private static volatile AppInitializer sInstance;
    
    // Obtain the singleton AppInitializer instance
    @NonNull
    @SuppressWarnings("UnusedReturnValue")
    public static AppInitializer getInstance(@NonNull Context context) {
        if (sInstance == null) {
            synchronized (sLock) {
                if (sInstance == null) {
                    sInstance = newAppInitializer(context); }}}returnsInstance; }}Copy the code

AppInitializer. GetInstance (context) method, to get the singleton AppInitializer instance.

AppInitializer –> discoverAndInitialize method

public final class AppInitializer {

    // Tracing
    private static final String SECTION_NAME = "Startup";

    // The Set Set of Initializer has been found, and value is the Initializer class.
    @NonNull
    finalSet<Class<? extends Initializer<? >>> mDiscovered;// Discover and initialize
    @SuppressWarnings("unchecked")
    void discoverAndInitialize(a) {
        try {
            Trace.beginSection(SECTION_NAME);
            // Get the InitializationProvider ProviderInfo
            ComponentName provider = new ComponentName(mContext.getPackageName(),
                    InitializationProvider.class.getName());
            ProviderInfo providerInfo = mContext.getPackageManager()
                    .getProviderInfo(provider, GET_META_DATA);
            // Get 
      
        information
      
            Bundle metadata = providerInfo.metaData;
            // Get the string "androidx.startup"
            String startup = mContext.getString(R.string.androidx_startup);
            // Check whether metadata is null, that is, whether 
      
        is configured.
      
            if(metadata ! =null) {
                // Initialize the Class collection of InitializerSet<Class<? >> initializing =new HashSet<>();
                
       
       
         android:name
       
      
                Set<String> keys = metadata.keySet();
                // Traverses all keys in the metadata
                for (String key : keys) {
                    // Get the value of metadata, that is, get android:value in 
      
       .
      
                    String value = metadata.getString(key, null);
                    
      
        android:value = "androidx.startup"
      
                    if (startup.equals(value)) {
                        
      
        android:name specifies the class
      Class<? > clazz = Class.forName(key);// Check whether 
      
        android:name is a subclass of Initializer.
      
                        if (Initializer.class.isAssignableFrom(clazz)) {
                            // If Initializer is a subclass, the call will be strong.Class<? extends Initializer<? >> component = (Class<? extends Initializer<? >>) clazz;// Add to the mDiscovered collection
                            mDiscovered.add(component);
                            // Prints logs
                            if (StartupLogger.DEBUG) {
                                StartupLogger.i(String.format("Discovered %s", key));
                            }
                            // Initialize component, that is, initialize the class specified by Android :name in 
      
       .
      
                            doInitialize(component, initializing);
                        }
                    }
                }
            }
        } catch (PackageManager.NameNotFoundException | ClassNotFoundException exception) {
            throw new StartupException(exception);
        } finally{ Trace.endSection(); }}}Copy the code

AppInitializer. DiscoverAndInitialize () method, to find and initialization, Initializer, find all, Initializer list file configuration, and then call doInitialize initialized () method.

Description:

  1. <meta-data>Within theandroid:valueThat must beandroidx.startup.
  2. <meta-data>Within theandroid:nameThat must beInitializerA subclasstheClass name full path.
  3. inThe manifest fileConfiguration of theallInitializer, will be added tomDiscovered(Found)A collection of.

Let’s see AppInitializer. DoInitialize () method.

AppInitializer –> doInitialize method

public final class AppInitializer {
    
    / / thread lock
    private static final Object sLock = new Object();

    // The Map collection of Initializer has been initialized. Key is the class of Initializer and value is the return value of the initializer.oncreate () method.
    @NonNull
    finalMap<Class<? >, Object> mInitialized;// Do the initialization
    @NonNull
    @SuppressWarnings({"unchecked", "TypeParameterUnusedInFormals"})
    <T> T doInitialize(
            @NonNullClass<? extends Initializer<? >> component,@NonNullSet<Class<? >> initializing) {
        // Synchronize to ensure thread safety.
        synchronized (sLock) {
            boolean isTracingEnabled = Trace.isEnabled();
            try {
                if (isTracingEnabled) {
                    // Use the simpleName here because section names would get too big otherwise.
                    Trace.beginSection(component.getSimpleName());
                }
                // If the class is being initialized and then initialized, an exception is thrown.
                if (initializing.contains(component)) {
                    String message = String.format(
                            "Cannot initialize %s. Cycle detected.", component.getName()
                    );
                    throw new IllegalStateException(message);
                }
                / / the result
                Object result;
                // Determine whether the class has been initialized to prevent repeated initialization.
                if(! mInitialized.containsKey(component)) {// If it is not initialized, initialize it and record the result of its create.
                    // Add to the collection being initialized, tag being initialized.
                    initializing.add(component);
                    try {
                        // reflection creates objects
                        Object instance = component.getDeclaredConstructor().newInstance();
                        Component implements Initializer, so no problem.Initializer<? > initializer = (Initializer<? >) instance;// Get the list of initializers it depends onList<Class<? extends Initializer<? >>> dependencies = initializer.dependencies();If the Initializer list it depends on is not empty, it initializes the list first.
                        if(! dependencies.isEmpty()) {// Iterate over the list of initializers it depends on
                            for(Class<? extends Initializer<? >> clazz : dependencies) {// Check whether the Initializer it depends on has already been initialized.
                                if(! mInitialized.containsKey(clazz)) {// if it is not initialized, it is initialized recursively.
                                    // - Note: The following code is executed after all dependencies are completed.doInitialize(clazz, initializing); }}}// Print log: component name in initialization.
                        if (StartupLogger.DEBUG) {
                            StartupLogger.i(String.format("Initializing %s", component.getName()));
                        }
                        // Call the create() method to initialize and record its return.
                        result = initializer.create(mContext);
                        // Print logs: indicates the name of the component whose initialization is completed.
                        if (StartupLogger.DEBUG) {
                            StartupLogger.i(String.format("Initialized %s", component.getName()));
                        }
                        // Remove from the initializing collection, the flag has been initialized.
                        initializing.remove(component);
                        // Store the initialized Initializer and the result returned by its create() method.
                        mInitialized.put(component, result);
                    } catch (Throwable throwable) {
                        throw newStartupException(throwable); }}else {
                    Get the result of create() if it is already initialized.
                    result = mInitialized.get(component);
                }
                // Returns the result of initializer.create()
                return (T) result;
            } finally{ Trace.endSection(); }}}}Copy the code

AppInitializer. DoInitialize () method, as a real initialization, Initializer method, it reflected, Initializer objects, and invoke the create () method to notify the internal initialization, and returns the create () method returns a value.

Description:

  1. If theInitializerNot initialized,Reflection to createthisInitializerObject and call itcreate()methodsNotifies internal initializationAnd should becreate()methodsThe results ofAdded to themInitializedFor subsequent acquisition;Otherwise,, frommInitializedTo deriveInitialization for the first timetheThe resulting valueTo make itDon’t create it very oftenthisInitializerObject.
  2. If you want to createInitializerDepend on othersInitializer, will proceedLoop recursive initializationotherInitializerTo make otherInitializerAll initialization is completeIs executedInitializerthecreate()Methods.
  3. doInitialize()Method, because usedsynchronizedCode block, and locksLockforstaticOf (only), so when there are threadsisperformdoInitialize()Method is executed by another threaddoInitialize()Both methodsWait for the last thread to complete.

Manually initialize components

AppInitializer –> initializeComponent method

// Initialize the Initializer class
@NonNull
@SuppressWarnings("unused")
public <T> T initializeComponent(@NonNull Class<? extends Initializer<T>> component) {
    return doInitialize(component, newHashSet<Class<? > > ()); }Copy the code

AppInitializer. InitializeComponent () method, direct call doInitialize () method to create, and returns its doInitialize () method of the resulting value (namely, Initializer. The create () method return values).

Description:

  1. onlyinitializeComponent()Method to getInitializerthecreate()Method, regardless of whetherAutomatic initialization(ContentProvider)deposit, orManual initialization(callinitializeComponent())deposit, can beOnce again,callinitializeComponent()methodsAccess to theIn thecreate()methodsThe return value.
  2. Due to thedoInitialize()Method, usedsynchronized, resulting inIts implementationEverything else has to wait. Such as:A,BTime consumingsoRespectively in a child threadperforminitializeComponent()Method,CinThe main threadExecute to make it three separately inThree threadsExecute to achieveconcurrentBut the result is:AThe execution of,BWaiting for theAExecution completed,CWaiting for theA,BExecution completed, not reachedconcurrentThe effect.

Other source

AppInitializer –> isEagerlyInitialized method

Public Boolean isEagerlyInitialized(@nonnull Class<? extends Initializer<? >> Component) {// If discoverAndInitialize() has never been called, there is no rush to initialize anything. return mDiscovered.contains(component); }Copy the code

AppInitializer. IsEagerlyInitialized () method, is eagerly for, Initializer is initialized, whether that is in the listing file configuration.

The advantages and disadvantages

advantages

  • Provides a rule that can be used by all three librariesThe sameContentProviderIn itsInside the libraryforInitialize the.Reduced initialization codeandTo optimize thetheCreate multipleContentProviderCaused by thePerformance, time loss.
  • Supports initialization order and dependencies.

disadvantages

  • Multithreaded concurrent initialization is not supported.
  • Reflection to createInitializerClass that has a slight impact on performance.

conclusion

Jetpack-startup source code Jetpack’s other source code series will be published later, please stay tuned. If you have any questions, see you in the comments!

Finally, I would like to recommend my website, devbolg.cn, the developer’s technical blog, which currently contains android related technologies, and will be open to all developers later. Welcome to experience!