App StartUp源码分析

1 使用

  • step1

在build.gradle里添加依赖: implementation “androidx.startup:startup-runtime:1.0.0-alpha02″,注意需要项目使用andridX,如果没使用的话,需要先在gradle.properties里添加:
android.useAndroidX=true

  • step2

建立一个初始化类,需要实现androidx.startup.Initializer,并实现里面的两个方法:

public class ToolsInitializer implements Initializer<Boolean> {

    @NonNull
    @Override
    public Boolean create(Context context) {
        //可以在这里获取到Context对象,来进行初始化
        return ShareTools.init(context);
    }

    @NonNull
    @Override
    public List<Class<? extends Initializer<?>>> dependencies() {
        //这里返回当前初始化项目之前需要初始化的项目,比如当前初始化项需要先初始化BaseTools,那么我就将初始化BaseTools的BaseInitializer添加进来
        List<Class<? extends Initializer<?>>> list = new ArrayList<>();
        list.add(BaseInitializer.class);
        return list;
    }
}

//这个类初始化BaseTools
public class BaseInitializer implements Initializer<Boolean> {
    @NonNull
    @Override
    public Boolean create(Context context) {
        BaseTools.init(context);
        //不需要返回值的,随便返回一个值就行
        return true;
    }

    @NonNull
    @Override
    public List<Class<? extends Initializer<?>>> dependencies() {
        //没有前置依赖,返回空列表
        return Collections.emptyList();
    }
}
复制代码
  • step3

在清单文件中添加

<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" //只需要改这里的名字就行
        android:value="androidx.startup" />
</provider>
复制代码

这样过后,app启动后,会找到androidx.startup.InitializationProvider并获取meta列表,依次执行他们的create方法去初始化,如果dependencies返回不为空,则先执行他们的create方法,
上述清单文件只添加了ToolsInitializer,但是由于ToolsInitializer的dependencies返回的list中含有BaseInitializer,所以BaseInitializer会被先初始化.

当然也可以:

<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" //这个在后面,后初始化
        android:value="androidx.startup" />
</provider>
复制代码

这两种方式的结果是一样的

  • 延迟初始化

如果想将某一个初始化器延迟执行,只需要

  • 1: 将node=”remove”即可,如下,将ToolsInitializer延迟初始化:
 <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"
        tools:node="remove" //这样就可以避免在app启动时就初始化
        android:value="androidx.startup" />
</provider>
复制代码
  • 2: 在需要初始化的地方执行
AppInitializer.getInstance(this).initializeComponent(ToolsInitializer.class);
复制代码

即可
如果想要所有的初始化器都延迟初始化,可以直接将标签的node=”remove”即可,如:

 <provider
    android:name="androidx.startup.InitializationProvider"
    android:authorities="${applicationId}.androidx-startup"
    android:exported="false"
    tools:node="remove"> //这样就可以避免此<provider>下的所有初始化器在app启动时就初始化
    <meta-data
        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>
复制代码

然后跟上面一样,在需要的地方执行

AppInitializer.getInstance(this).initializeComponent(xxxx);
复制代码

即可

2 源码分析

首先我们需要了解一个知识点: ContentProvider的onCreate()是在Application的onCreate()之前执行的,大家可以自行谷歌

首先我们看清单文件,发现里面的是:androidx.startup.InitializationProvider,我们直接看它的源码,这里只看关键部分

public final class InitializationProvider extends ContentProvider {
    @Override
    public boolean onCreate() {
        Context context = getContext();
        if (context != null) {
            //使用单利模式,调用了discoverAndInitialize(),追!
            AppInitializer.getInstance(context).discoverAndInitialize();
        } else {
            throw new StartupException("Context cannot be null");
        }
        return true;
    }

    ......
}
复制代码

AppInitializer.discoverAndInitialize()

void discoverAndInitialize() {
    try {
        //获取provider
        ComponentName provider = new ComponentName(mContext.getPackageName(),InitializationProvider.class.getName());
        //获取<meta-data>
        ProviderInfo providerInfo = mContext.getPackageManager().getProviderInfo(provider, GET_META_DATA);
        Bundle metadata = providerInfo.metaData;
        //注意这个标签,就是"androidx.startup"
        String startup = mContext.getString(R.string.androidx_startup);
        //遍历<meta-data>集合
        if (metadata != null) {
            Set<Class<?>> initializing = new HashSet<>();
            Set<String> keys = metadata.keySet();
            for (String key : keys) {
                //获取<meta-data>里面的android:value值
                String value = metadata.getString(key, null);
                //如果值是"androidx.startup"
                if (startup.equals(value)) {
                    //就根据key(也就是<meta-data>里面的name),来反射获取类
                    Class<?> clazz = Class.forName(key);
                    if (Initializer.class.isAssignableFrom(clazz)) {
                        //获取class对象
                        Class<? extends Initializer<?>> component =(Class<? extends Initializer<?>>) clazz;
                        //执行初始化
                        doInitialize(component, initializing);
                    }
                }
            }
        }
    } catch (PackageManager.NameNotFoundException | ClassNotFoundException exception) {
        throw new StartupException(exception);
    } finally {
        Trace.endSection();
    }
}
复制代码

上述逻辑很清晰,直接获取列表,然后根据value是否等于”androidx.startup”来判断是否根据name进行反射初始化,比如:

 <meta-data
    android:name="com.example.androidxtest.BaseInitializer"
    android:value="androidx.startup" />
复制代码

先将上面的解析为key-value形式,key为”com.example.androidxtest.BaseInitializer”,value为”androidx.startup”,发现value就是”androidx.startup”,于是就将key作为className,通过反射获取到我们的BaseInitializer来进行初始化,所以这里的name一定要写成完整类名

接下来看AppInitializer.doInitialize

<T> T doInitialize(@NonNull Class<? extends Initializer<?>> component,@NonNull Set<Class<?>> initializing) {
    synchronized (sLock) {
        try {
            //如果正在初始化这个类,就抛出异常
            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();
                    //强转,因为implements了Initializer,所以没问题
                    Initializer<?> initializer = (Initializer<?>) instance;
                    //获取dependencies列表
                    List<Class<? extends Initializer<?>>> dependencies =initializer.dependencies();
                    //如果列表不为空,就先初始化列表,这印证了我们上面的说法
                    if (!dependencies.isEmpty()) {
                        for (Class<? extends Initializer<?>> clazz : dependencies) {
                            if (!mInitialized.containsKey(clazz)) {
                                doInitialize(clazz, initializing);
                            }
                        }
                    }
                    //调用create(Context)方法
                    result = initializer.create(mContext);
                    //标记为已经初始化过(从正在初始化列表移除即可)
                    initializing.remove(component);
                    //将初始化结果缓存起来
                    mInitialized.put(component, result);
                } catch (Throwable throwable) {
                    throw new StartupException(throwable);
                }
            } else {
                //如果已经初始化过了,就走这里,直接从缓存列表中返回结果 
                result = mInitialized.get(component);
            }
            //初始化完毕,返回结果
            return (T) result;
        } finally {
            Trace.endSection();
        }
    }
}
复制代码

完事,上述静态初始化逻辑很简单,现在我们来看动态初始化的

AppInitializer.getInstance(this).initializeComponent(ToolsInitializer.class);

 @NonNull
@SuppressWarnings("unused")
public <T> T initializeComponent(@NonNull Class<? extends Initializer<T>> component) {
    //直接调用了doInitialize()
    return doInitialize(component, new HashSet<Class<?>>());
}
复制代码

动态初始化,直接调用了doInitialize(),也就是跨过了解析的过程,也就没什么说了

Tips: node=”remove”在清单文件合并时,会移除这个节点,所以就不会被解析到集合中,也就不会被初始化

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享