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”在清单文件合并时,会移除这个节点,所以就不会被解析到集合中,也就不会被初始化