[Jetpack组件] StartUp从使用到解析

1,700 阅读3分钟

StartUp

1. Application初始化

startup主要作用是优化app启动流程,实现更优雅的初始化操作。通常我们使用第三方组件的时候都需要在Application中进行初始化,比如:

class App : BaseApp(){

    override fun onCreate() {
        super.onCreate()
       
        A.init()
        B.init(this,"xx")
        XXX.init(this)
    }

}

如果在application中初始化的组件多,就会消耗更多的时间。所以Jetpack引入了新的组件StartUp

2. StartUp的使用

首先需要引入依赖:

dependencies {
    implementation "androidx.startup:startup-runtime:1.1.0"
}

自己实现Initializer接口,这个接口很简单就两个方法:

class AppInit : Initializer<Unit> {

    override fun create(context: Context) {
        //init...
    }

    override fun dependencies(): List<Class<out Initializer<*>>> = emptyList()
}
  • onCreat方法中执行初始化操作
  • dependencies主要实现依赖其他Initializer,如果有其他Initializer,需先执行其他Initializer,再执行本Initializer。

然后在AndroidMainfest.xml中加入如下:

    <application>

        ...
        
        <provider
            android:name="androidx.startup.InitializationProvider"
            android:authorities="${applicationId}.androidx-startup"
            android:exported="false"
            tools:node="merge">
            <meta-data  android:name="com.example.baselibrary.AppInit"
                android:value="androidx.startup" />
        </provider>
        
    </application>

除了android:name="com.example.baselibrary.AppInit"需要改成自定义的 Initializer名称外,其他都不需要做啥改变

总结就是三步,非常简单:

  1. 引入App Startup的库。
  2. 自定义一个用于初始化的Initializer。
  3. 将自定义Initializer配置到AndroidManifest.xml当中。

如果不需要初始化,可以自行定义:

        <!--标记为不初始化-->
        <meta-data
			android:name="com.example.baselibrary.AppInit"
			tools:node="remove" />

然后在需要的时候动态执行:

AppInitializer.getInstance(this).initializeComponent(AppInit::class.java)

3. StartUp启动流程

我们知道app启动由AMS负责,由ActivityThread来创建并启动application,在application执行onCreat之前还有其它组件,比如contentProvider会执行初始化,startup就是在contentprovider中进行初始化的。

application与contentprovider初始化顺序:

Application 构造方法 –> Application.attachBaseContext –> ContentProvider 构造方法 –> ContentProvider.onCreate –> Application.onCreate –> Activity 构造方法 –> Activity attachBaseContext –> Activity.onCreate

startup在contentprovider的onCreat方法中初始化:

public class InitializationProvider extends ContentProvider {

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

discoverAndInitialize就是执行Initializer初始化的方法:

    void discoverAndInitialize() {
        try {
            Trace.beginSection(SECTION_NAME);
            ComponentName provider = new ComponentName(mContext.getPackageName(),
                    InitializationProvider.class.getName());
            ProviderInfo providerInfo = mContext.getPackageManager()
                    .getProviderInfo(provider, GET_META_DATA);
            Bundle metadata = providerInfo.metaData;
            String startup = mContext.getString(R.string.androidx_startup);
            if (metadata != null) {
                Set<Class<?>> initializing = new HashSet<>();
                Set<String> keys = metadata.keySet();
                for (String key : keys) {
                    String value = metadata.getString(key, null);
                    if (startup.equals(value)) {
                        Class<?> clazz = Class.forName(key);
                        if (Initializer.class.isAssignableFrom(clazz)) {
                            Class<? extends Initializer<?>> component =
                                    (Class<? extends Initializer<?>>) clazz;
                            mDiscovered.add(component);
                            if (StartupLogger.DEBUG) {
                                StartupLogger.i(String.format("Discovered %s", key));
                            }
                            //最终在这里执行
                            doInitialize(component, initializing);
                        }
                    }
                }
            }
        } catch (PackageManager.NameNotFoundException | ClassNotFoundException exception) {
            throw new StartupException(exception);
        } finally {
            Trace.endSection();
        }
    }

doInitialize()是最终执行方法:

    @SuppressWarnings({"unchecked", "TypeParameterUnusedInFormals"})
    <T> T doInitialize(
            @NonNull Class<? extends Initializer<?>> component,
            @NonNull Set<Class<?>> initializing) {
        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 (initializing.contains(component)) {
                    String message = String.format(
                            "Cannot initialize %s. Cycle detected.", component.getName()
                    );
                    throw new IllegalStateException(message);
                }
                Object result;
                if (!mInitialized.containsKey(component)) {
                    initializing.add(component);
                    try {
                        Object instance = component.getDeclaredConstructor().newInstance();
                        Initializer<?> initializer = (Initializer<?>) instance;
                        List<Class<? extends Initializer<?>>> dependencies =
                                initializer.dependencies();

                        if (!dependencies.isEmpty()) {
                            for (Class<? extends Initializer<?>> clazz : dependencies) {
                                if (!mInitialized.containsKey(clazz)) {
                                    doInitialize(clazz, initializing);
                                }
                            }
                        }
                        if (StartupLogger.DEBUG) {
                            StartupLogger.i(String.format("Initializing %s", component.getName()));
                        }
                        //回调creat方法
                        result = initializer.create(mContext);
                        if (StartupLogger.DEBUG) {
                            StartupLogger.i(String.format("Initialized %s", component.getName()));
                        }
                        initializing.remove(component);
                        //使用map保存
                        mInitialized.put(component, result);
                    } catch (Throwable throwable) {
                        throw new StartupException(throwable);
                    }
                } else {
                    result = mInitialized.get(component);
                }
                return (T) result;
            } finally {
                Trace.endSection();
            }
        }
    }

我们发现调用doInitialize的地方除了discoverAndInitialize还有一个initializeComponent,这个方法并没有被调用,是干啥的?其实是我们可以手动调用它,延迟startup的初始化。

    @SuppressWarnings("unused")
    public <T> T initializeComponent(@NonNull Class<? extends Initializer<T>> component) {
        return doInitialize(component, new HashSet<Class<?>>());
    }

4. 完结撒花

至此,Startup的介绍也就完成了,对于其他单独模块或者第三方引入词库还是非常不错的,可以做到无感知的初始化。

参考项目:mvvm_develop

之前的文章介绍了网络封装、组件化、基础工具等,有兴趣的可以查看:

还有一些关于UI的基础工具: