Jetpack 之 App Startup 源码分析
抛砖引玉
使用过 LeakCanary
的童鞋都知道,早在 LeakCanary
1.x 版本的时候,我们需要在 Application
中手动调用 install 方法来完成 LeakCanary
的初始化过程。
而 LeakCanary
2.x 的版本实现了自动初始化。通过查看 LeakCanary
2.x 的源码,我们可以发现,它通过继承 ContentProvider
,在 onCreate()
中来完成初始化过程。
internal sealed class AppWatcherInstaller : ContentProvider() {
override fun onCreate(): Boolean {
val application = context!!.applicationContext as Application
val leakCanaryEnable = application
.getSharedPreferences(KEY_LEAK_CANARY_ENABLE_FILE, Context.MODE_PRIVATE)
.getBoolean(KEY_LEAK_CANARY_ENABLE, false)
if (!leakCanaryEnable) {
Log.d("LeakCanary", "Sorry ! you can't init LeakCanary")
return true
}
InternalAppWatcher.install(application)
return true
}
}
为什么使用 ContentProvider
就能够达到自动初始化呢?
在应用程序启动过程中,会调用到 ActivityThread.handleBindApplication()
,我们可以看到如下关键代码。
// 1. 这里会反射创建 Application, 并调用 Application.attach()
app = data.info.makeApplication(data.restrictedBackupMode, null);
if (!data.restrictedBackupMode) {
if (!ArrayUtils.isEmpty(data.providers)) {
// 2. 这里会装载 ContentProvider
installContentProviders(app, data.providers);
}
}
try {
// 3. 这里会调用Application 的 onCreate()
mInstrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
}
通过源码,我们知道它的调用顺序如下。
- 创建
Application
,调用Application.attach()
; - 装载
ContentProvider
; - 调用
Application.onCreate()
;
所以在 ContentProvider.onCreate()
里面是可以拿到 application context , 在这里进行初始化是没有问题的。
而 本文介绍的 Jetpack 的新成员 App Startup
就是通过 ContentProvider
来完成实现的。
App Startup 的使用
使用 App Startup 库,我们需要在 build.gradle
中添加依赖。
implementation "androidx.startup:startup-runtime:1.0.0-alpha01"
实现 Initializer
接口,为组件创建初始化程序。
举个栗子,这里为 LibraryA 创建初始化程序,dependencies()
返回了一个集合,表明它依赖于其他库的初始化。
class LibraryAInitializer : Initializer<LibraryA> {
override fun create(context: Context): LibraryA {
return LibraryA.getInstance(context)
}
override fun dependencies(): MutableList<Class<out Initializer<*>>> {
return arrayListOf()
}
}
在 manifest.xml
文件中配置 InitializationProvider
, App Startup 使用指定的 ContentProvider
来检索和初始化 Initializer
。
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
</provider>
每个 Initializer
使用 <meta-data />
节点定义,其 value 必须为 androidx.startup
(这里后面源码会讲到)。
<meta-data
android:name="com.ppdai.code.jetpack.startup.initializer.LibraryAInitializer"
android:value="androidx.startup" />
这样,在 app 启动过程中就会自动初始化 LibraryA 这个库,是不是非常的简单。
假设 LibraryA 的初始化依赖于 LibraryB,我们只需要编写 LibraryB 的 Initializer
,然后修改 LibraryAInitializer,在其 dependencies()
方法中返回 LibraryB 的 Initializer
即可。
class LibraryAInitializer : Initializer<LibraryA> {
override fun create(context: Context): LibraryA {
return LibraryA.getInstance(context)
}
override fun dependencies(): MutableList<Class<out Initializer<*>>> {
return arrayListOf(LibraryBInitializer::class.java)
}
}
class LibraryBInitializer : Initializer<LibraryB> {
override fun create(context: Context): LibraryB {
return LibraryB.getInstance(context)
}
override fun dependencies(): MutableList<Class<out Initializer<*>>> {
return arrayListOf()
}
}
这样在初始化 LibraryA 的时候会先初始化 LibraryB。
如果不希望在 app 启动的时候初始化,我们也可以通过 AppInitializer.initializeComponent()
在代码中对指定的组件进行初始化,如下所示。
AppInitializer.getInstance(this).initializeComponent(LibraryAInitializer::class.java)
源码分析
InitializationProvider
InitializationProvider
在Application.onCreate()
调用之前检索和初始化Initializer
我们来看看 InitializationProvider
的具体实现,可以发现它只是在 onCreate()
方法中调用了 AppInitializer
的 discoverAndInitialize()
。
public final class InitializationProvider extends ContentProvider {
@Override
public boolean onCreate() {
Context context = getContext();
if (context != null) {
AppInitializer.getInstance(context).discoverAndInitialize();
} else {
throw new StartupException("Context cannot be null");
}
return true;
}
@Nullable
@Override
public 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 String getType(@NonNull Uri uri) {
throw new IllegalStateException("Not allowed.");
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
throw new IllegalStateException("Not allowed.");
}
@Override
public int delete(
@NonNull Uri uri,
@Nullable String selection,
@Nullable String[] selectionArgs) {
throw new IllegalStateException("Not allowed.");
}
@Override
public int update(
@NonNull Uri uri,
@Nullable ContentValues values,
@Nullable String selection,
@Nullable String[] selectionArgs) {
throw new IllegalStateException("Not allowed.");
}
}
AppInitializer
AppInitializer
是真正对Initializer
进行初始化的地方。
AppInitializer
可以拆分为三个部分。
-
AppInitializer
的创建; - 如何初始化在
manifest.xml
中配置的Initializer
; - 如何在代码中初始化指定
Initializer
。
创建
AppInitializer
使用单例模式。成员变量mInitialized
用来存放已经初始化的Initializer
,保证所有的Intializer
只被初始化一次。
private static AppInitializer sInstance;
// 用来存放已初始化的 Initializer
@NonNull
final Map<Class<?>, Object> mInitialized;
@NonNull
final Context mContext;
AppInitializer(@NonNull Context context) {
mContext = context.getApplicationContext();
mInitialized = new HashMap<>();
}
@NonNull
@SuppressWarnings("UnusedReturnValue")
public static AppInitializer getInstance(@NonNull Context context) {
synchronized (sLock) {
if (sInstance == null) {
sInstance = new AppInitializer(context);
}
return sInstance;
}
}
初始化 manifest.xml 中配置的 Initializer
这个过程主要是通过
discoverAndInitialize
这个方法来完成的
void discoverAndInitialize() {
try {
Trace.beginSection(SECTION_NAME);
// 1. 获取InitializationProvider在manifest中配置的meta-data
ComponentName provider = new ComponentName(mContext.getPackageName(),
InitializationProvider.class.getName());
ProviderInfo providerInfo = mContext.getPackageManager()
.getProviderInfo(provider, GET_META_DATA);
Bundle metadata = providerInfo.metaData;
// androidx.startup
String startup = mContext.getString(R.string.androidx_startup);
if (metadata != null) {
Set<Class<?>> initializing = new HashSet<>();
Set<String> keys = metadata.keySet();
// 2. 遍历meta-data,找到所有value="androidx.startup"的元素,进行初始化
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;
if (StartupLogger.DEBUG) {
StartupLogger.i(String.format("Discovered %s", key));
}
// 3. 初始化指定的 Initializer
doInitialize(component, initializing);
}
}
}
}
} catch (PackageManager.NameNotFoundException | ClassNotFoundException exception) {
throw new StartupException(exception);
} finally {
Trace.endSection();
}
}
可以看出,通过解析 meta-data
,我们就能拿到配置的 Initializer
,然后逐一调用 doInitialize()
进行初始化。
再看看 doInitialize
做了什么事情。
<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;
// 1. 保证 Initializer 只初始化一次
if (!mInitialized.containsKey(component)) {
initializing.add(component);
try {
// 2. 通过反射创建 Initializer
Object instance = component.getDeclaredConstructor().newInstance();
Initializer<?> initializer = (Initializer<?>) instance;
// 3. 获取 Initializer 的依赖关系
List<Class<? extends Initializer<?>>> dependencies =
initializer.dependencies();
// 4. 递归初始化,先初始化 Initializer 依赖的且没有被初始化过的 Initializer
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()));
}
// 5. 初始化自身
result = initializer.create(mContext);
if (StartupLogger.DEBUG) {
StartupLogger.i(String.format("Initialized %s", component.getName()));
}
initializing.remove(component);
// 6. 将 Initializer 放入已初始化 Map
mInitialized.put(component, result);
} catch (Throwable throwable) {
throw new StartupException(throwable);
}
} else {
result = mInitialized.get(component);
}
return (T) result;
} finally {
Trace.endSection();
}
}
}
在代码中初始化指定的Initializer
我们可以通过
AppInitializer
的initializeComponent()
在代码中初始化指定的Initializer
。
public <T> T initializeComponent(@NonNull Class<? extends Initializer<T>> component) {
return doInitialize(component, new HashSet<Class<?>>());
}
可以看到,它实际上也是调用了 doInitialize()
方法。
Initializer
用来在 app startup 过程中初始化 libraries
public interface Initializer<T> {
// application context
@NonNull
T create(@NonNull Context context);
// 返回 Initializer 的依赖。比如初始化 A,A 依赖于B,那么在初始化 A 的时候会优先查看 B 是否初始化了,如果没有初始化 B,则会先初始化 B。
@NonNull
List<Class<? extends Initializer<?>>> dependencies();
}