[Jetpack][App Startup]源码分析
简介
App Startup是Jetpack组件之一。可以阅读官方文档了解基本使用方式。
App Startup提供了一个Initializer接口将各个初始化工作隔离开来:
/**
* {@link Initializer}s can be used to initialize libraries during app startup, without
* the need to use additional {@link android.content.ContentProvider}s.
*
* @param <T> The instance type being initialized
*/
public interface Initializer<T> {
/**
* Initializes and a component given the application {@link Context}
*
* @param context The application context.
*/
@NonNull
T create(@NonNull Context context);
/**
* @return A list of dependencies that this {@link Initializer} depends on. This is
* used to determine initialization order of {@link Initializer}s.
* <br/>
* For e.g. if a {@link Initializer} `B` defines another
* {@link Initializer} `A` as its dependency, then `A` gets initialized before `B`.
*/
@NonNull
List<Class<? extends Initializer<?>>> dependencies();
}
其中:
- create方法中进行当前的初始化工作
- dependencies方法返回当前初始化需要的依赖项。依赖项是一个Initializer接口列表,依赖项会先于当前Initializer执行
通过Initializer接口,可以把复杂的初始化工作串起来,不过串起来也需要我们指定初始执行的Initializer。有两种方式可以指定初始执行的Initializer。
- 通过content-provider声明的方式
- 通过AppInitializer指定
content-provider的方式想来在实际项目会用的少一些,毕竟还是代码的控制力度更好一些。通过AppInitializer指定初始Initializer方式如下:
val result = AppInitializer.getInstance(this.applicationContext)
.initializeComponent(TestInitializer::class.java)
result即可Initializer产生的结果。
接下来就从AppInitializer着手分析App Startup的源码。
源码分析
AppInitializer初始化
首先AppInitializer是一个单例类,使用懒汉方式实现的单例:
public static AppInitializer getInstance(@NonNull Context context) {
if (sInstance == null) {
synchronized (sLock) {
if (sInstance == null) {
sInstance = new AppInitializer(context);
}
}
}
return sInstance;
}
@NonNull
final Map<Class<?>, Object> mInitialized;
@NonNull
final Set<Class<? extends Initializer<?>>> mDiscovered;
AppInitializer(@NonNull Context context) {
mContext = context.getApplicationContext();
mDiscovered = new HashSet<>();
mInitialized = new HashMap<>();
}
我们主要关注构造的过程,可以看到构造了一个Map和一个Set,也很好理解,毕竟多个Initializer总是需要容器保存的。其中:
- mInitialized保存已经完成初始化的Initializer。每个Initializer只能被初始化一次,简单说即其create方法只会被调用一次。其中Key为Initializer的class对象,value为Initializer的create返回值。
- mDiscovered保存发现的Initializer。当使用content-provider方式配置Initializer,则AppInitializer会根据content-provider内部的meta-data自动的发现关联的Initializer。
### Initializer初始化
/**
* Initializes a {@link Initializer} class type.
*
* @param component The {@link Class} of {@link Initializer} to initialize.
* @param <T> The instance type being initialized
* @return The initialized instance
*/
@NonNull
@SuppressWarnings("unused")
public <T> T initializeComponent(@NonNull Class<? extends Initializer<T>> component) {
return doInitialize(component, new HashSet<Class<?>>());
}
@NonNull
@SuppressWarnings({"unchecked", "TypeParameterUnusedInFormals"})
<T> T doInitialize(
@NonNull Class<? extends Initializer<?>> component,
@NonNull Set<Class<?>> initializing) {
//可以看到加了一个很粗的锁
//先不论性能,这里至少保证了初始化过程是线程安全的
synchronized (sLock) {
try {
//这里的代码主要是为了防止Initializer的循环依赖
if (initializing.contains(component)) {
String message = String.format(
"Cannot initialize %s. Cycle detected.", component.getName()
);
throw new IllegalStateException(message);
}
Object result;
//已完成初始化的Initializer不会被再次初始化
//那么多次调用initializeComponent并不会带来多大的性能损耗
if (!mInitialized.containsKey(component)) {
initializing.add(component);
try {
//反射创建Initializer的实例
Object instance = component.getDeclaredConstructor().newInstance();
Initializer<?> initializer = (Initializer<?>) instance;
//获取Initializer的依赖项
List<Class<? extends Initializer<?>>> dependencies =
initializer.dependencies();
if (!dependencies.isEmpty()) {
for (Class<? extends Initializer<?>> clazz : dependencies) {
if (!mInitialized.containsKey(clazz)) {
//开始递归过程
//保证依赖先于Initializer先被初始化
doInitialize(clazz, initializing);
}
}
}
//真正执行Initializer的初始化
//Initializer的初始化是在当前线程执行的,并没有什么异步处理
//如果在主线程调用初始化,那么初始化工作也会在主线程执行
result = initializer.create(mContext);
//更新容器
initializing.remove(component);
mInitialized.put(component, result);
} catch (Throwable throwable) {
throw new StartupException(throwable);
}
} else {
//如果Initializer已经初始化,则直接返回结果
result = mInitialized.get(component);
}
return (T) result;
} finally {
Trace.endSection();
}
}
}
总结
目前App Startup还很简单,没什么卵用。