方式一:使用 Application#onCreate 進(jìn)行初始化
使用方式
- 自定義
CustomApplication
class CustomApplication : Application() {
// ..
override fun onCreate() {
super.onCreate()
// 進(jìn)行組件初始化
}
// ..
}
- 在
主module清單文件中聲明使用CustomApplication
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<!-- ... -->
<application android:name="CustomApplication全路徑或者相對(duì)于資源的路徑">
<!-- ... -->
</application>
</manifest>
優(yōu)缺點(diǎn)
- 優(yōu)點(diǎn):簡(jiǎn)單易用,只需在
主module的Application#onCreate進(jìn)行組件初始化,并且可以指定組件間初始化順序; - 缺點(diǎn):在其他
主module使用的時(shí)候,需要在自身Application#onCreate進(jìn)行一遍初始化(對(duì)應(yīng)組件依賴(lài)方來(lái)說(shuō),較為繁瑣);同時(shí)如果組件存在依賴(lài)關(guān)系,使用方要清楚組件之間的關(guān)系,從而確定組件初始化順序,增加組件使用成本;
方式二:使用 Content Provider 進(jìn)行初始化
使用方式
- 自定義
ContentProvider
class CustomProvider : ContentProvider() {
override fun onCreate(): Boolean {
// 初始化組件
return true
}
override fun query(
uri: Uri,
projection: Array<out String>?,
selection: String?,
selectionArgs: Array<out String>?,
sortOrder: String?
): Cursor? = null
override fun getType(uri: Uri): String? = null
override fun insert(uri: Uri, values: ContentValues?): Uri? = null
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int = 0
override fun update(uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array<out String>?
): Int = 0
}
- 自定義
ContentProvider在當(dāng)前組件AndroidManifest.xml進(jìn)行聲明
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<!-- ... -->
<application>
<!--
1. name 為 CustomProvider 的全路徑,或者相對(duì)于資源文件的路徑
2. authorities 加上 ${applicationId} 可以有效避免多個(gè)應(yīng)用依賴(lài)該組件重復(fù),導(dǎo)致不能安裝問(wèn)題
3. exported = false,讓外部不能使用,純粹只是為了當(dāng)前組件的初始化
-->
<provider
android:name="CustomProvider的路徑"
android:authorities="${applicationId}.custom-startup"
android:exported="false" />
<!-- ... -->
</application>
</manifest>
優(yōu)缺點(diǎn)
- 優(yōu)點(diǎn):在應(yīng)用啟動(dòng)的時(shí)候,系統(tǒng)會(huì)調(diào)用
ContentProvider#onCreate進(jìn)行初始化,則避免組件使用方在主module中Application#onCreate初始化,對(duì)于組件使用方來(lái)說(shuō)是無(wú)感知的; - 缺點(diǎn):如果組件間初始化是有依賴(lài)性,則多個(gè)
Provider在清單文件中的順序是有要求的,但這個(gè)是非常困難進(jìn)行調(diào)整的,并且很容易錯(cuò);同時(shí)Provider的實(shí)例化成本高昂,在不必要的情況下可能會(huì)拖慢啟動(dòng)序列
ContentProvider#onCreate 初始化路徑(基于android33)
- ActivityThread#main(
zygote新建進(jìn)程,執(zhí)行ActivityThread的main方法,執(zhí)行主Looper循環(huán)) - ActivityThread#attach(
ActivityThread初始化) - ActivityManagerService#attachApplication(
Binder調(diào)用,從應(yīng)用 =>ActivityManagerService) - ApplicationThread#bindApplication(
Binder調(diào)用,從ActivityManagerService=> 應(yīng)用) - H#handleMessage(BIND_APPLICATION)(使用
H進(jìn)行分發(fā)) - ActivityThread#handleBindApplication(當(dāng)前應(yīng)用綁定
application) - ActivityThread#installContentProviders(安裝多個(gè)
Provider) - ActivityThread#installProvider(安裝單個(gè)
Provider) - ContentProvider#attachInfo
- ContentProvider#onCreate
方式三:使用 Jetpack startup 組件庫(kù)進(jìn)行初始化
使用方式
- 在
module模塊的build.gradle引用Jetpack startup
dependencies {
implementation "androidx.startup:startup-runtime:1.1.1"
}
- 繼承
Initializer<*>,重寫(xiě)onCreate和dependencies方法,
class CustomInitializer : Initializer<Custom> {
override fun create(context: Context): Custom {
// Custom 初始化
return Custom.getInstance(context)
}
override fun dependencies(): List<Class<out Initializer<*>>> {
// 聲明當(dāng)前依賴(lài)的類(lèi)庫(kù)
return emptyList()
}
}
- 在模塊
AndroidManifest.xml中聲明
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<!-- ... -->
<application>
<!--
1. name 為固定 androidx.startup.InitializationProvider,則知道是用這個(gè)來(lái)進(jìn)行初始化的
2. authorities = "${applicationId}.androidx-startup" 用于避免多個(gè)應(yīng)用導(dǎo)致的重復(fù)
3. exported 僅供自身使用,不對(duì)外暴露
4. tools:node = "merge" 表示合并相同的 InitializationProvider,在這里代表合并其他組件的 meta-data
-->
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<!--
1. name 為 CustomInitializer 的全路徑,或者相對(duì)于資源文件的路徑
2. value 為固定的 androidx.startup,用于提取 CustomInitializer 的路徑
-->
<meta-data android:name="CustomInitializer的路徑"
android:value="androidx.startup" />
</provider>
<!-- ... -->
</application>
</manifest>
優(yōu)缺點(diǎn)
- 優(yōu)點(diǎn):解決方法二中的2個(gè)問(wèn)題;在整體應(yīng)用中,只存在一個(gè)
Provider,并且Initializer#dependencies中聲明該組件所依賴(lài)組件,在初始該組件的時(shí)候會(huì)先初始化該組件所依賴(lài)組件 - 缺點(diǎn):從
Initializer#dependencies方法參數(shù)可知,需要所依賴(lài)組件實(shí)現(xiàn)Initializer才可以,需要改動(dòng)原來(lái)的組件;但如果后續(xù)的組件都按照此來(lái)聲明,對(duì)于使用方來(lái)說(shuō),會(huì)更加簡(jiǎn)單
源碼解析
- 從
android:name可知InitializationProvider入口
// androidx.startup.InitializationProvider
public class InitializationProvider extends ContentProvider {
@Override
public final boolean onCreate() {
Context context = getContext();
if (context != null) {
Context applicationContext = context.getApplicationContext();
if (applicationContext != null) {
// 調(diào)用 AppInitializer 的 discoverAndInitialize
AppInitializer.getInstance(context).discoverAndInitialize();
} else {
StartupLogger.w("Deferring initialization because `applicationContext` is null.");
}
} else {
throw new StartupException("Context cannot be null");
}
return true;
}
// ...
}
AppInitializer#discoverAndInitialize
public final class AppInitializer {
private static volatile AppInitializer sInstance;
private static final Object sLock = new Object();
final Map<Class<?>, Object> mInitialized;
final Set<Class<? extends Initializer<?>>> mDiscovered;
final Context mContext;
@NonNull
public static AppInitializer getInstance(@NonNull Context context) {
// 雙重校驗(yàn)獲取 AppInitializer
if (sInstance == null) {
synchronized (sLock) {
if (sInstance == null) {
sInstance = new AppInitializer(context);
}
}
}
return sInstance;
}
AppInitializer(@NonNull Context context) {
mContext = context.getApplicationContext(); // application context
mDiscovered = new HashSet<>(); // 已發(fā)現(xiàn)的Initializer
mInitialized = new HashMap<>(); // 已初始化的內(nèi)容
}
void discoverAndInitialize() {
try {
// step1: 創(chuàng)建包含 InitializationProvider 類(lèi)信息的 ComponentName
ComponentName provider = new ComponentName(mContext.getPackageName(),
InitializationProvider.class.getName());
// step2: 獲取 InitializationProvider 對(duì)應(yīng)的 META_DATA 信息
ProviderInfo providerInfo = mContext.getPackageManager()
.getProviderInfo(provider, GET_META_DATA);
Bundle metadata = providerInfo.metaData;
// step3: 解析metadata
discoverAndInitialize(metadata);
} catch (PackageManager.NameNotFoundException exception) {
throw new StartupException(exception);
}
}
void discoverAndInitialize(@Nullable Bundle metadata) {
// step4: 獲取 startup 字符串
String startup = mContext.getString(R.string.androidx_startup);
try {
if (metadata != null) {
// step5: initializing 記錄正在初始化的類(lèi),主要用于防止循環(huán)引用
Set<Class<?>> initializing = new HashSet<>();
Set<String> keys = metadata.keySet();
for (String key : keys) {
String value = metadata.getString(key, null);
// step6: 這里限定了 android:value 只能為 startup 的值
if (startup.equals(value)) {
// step7: 反射獲取此類(lèi),并檢查是否是 Initializer 的子類(lèi)
Class<?> clazz = Class.forName(key);
if (Initializer.class.isAssignableFrom(clazz)) {
Class<? extends Initializer<?>> component =
(Class<? extends Initializer<?>>) clazz;
// step8: 記錄當(dāng)前類(lèi)進(jìn)入發(fā)現(xiàn)集合中
mDiscovered.add(component);
}
}
}
// step9: 遍歷當(dāng)前發(fā)現(xiàn)的類(lèi),開(kāi)始初始化
for (Class<? extends Initializer<?>> component : mDiscovered) {
// Tips: initializing 記錄正在初始化的類(lèi),這里為空集合
doInitialize(component, initializing);
}
}
} catch (ClassNotFoundException exception) {
throw new StartupException(exception);
}
}
@NonNull
@SuppressWarnings({"unchecked", "TypeParameterUnusedInFormals"})
private <T> T doInitialize(
@NonNull Class<? extends Initializer<?>> component,
@NonNull Set<Class<?>> initializing) {
try {
// step10: 如果 initializing 包含 component,則證明出現(xiàn)循環(huán)依賴(lài)
if (initializing.contains(component)) {
String message = String.format(
"Cannot initialize %s. Cycle detected.", component.getName()
);
throw new IllegalStateException(message);
}
Object result;
// step11: 檢查此類(lèi)是否已初始化,已初始化這直接返回,反之進(jìn)行初始化
if (!mInitialized.containsKey(component)) {
initializing.add(component);
try {
// step12: 使用反射調(diào)用構(gòu)造參數(shù);由這可知這里需要一個(gè)默認(rèn)的空參的構(gòu)造函數(shù)
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) {
// step13: 如果當(dāng)前依賴(lài)組件未進(jìn)行初始化,則進(jìn)行初始化
if (!mInitialized.containsKey(clazz)) {
doInitialize(clazz, initializing);
}
}
}
// step14: 調(diào)用 Initializer#create 創(chuàng)建組件
result = initializer.create(mContext);
// step15: 當(dāng)前組件初始化完成,從正在初始化集合移除和加入已初始化集合中
initializing.remove(component);
mInitialized.put(component, result);
} catch (Throwable throwable) {
throw new StartupException(throwable);
}
} else {
result = mInitialized.get(component);
}
return (T) result;
}
}
// ======================= 剩余方法 =======================
@NonNull
@SuppressWarnings("unused")
public <T> T initializeComponent(@NonNull Class<? extends Initializer<T>> component) {
return doInitialize(component);
}
public boolean isEagerlyInitialized(@NonNull Class<? extends Initializer<?>> component) {
return mDiscovered.contains(component);
}
@NonNull
@SuppressWarnings({"unchecked", "TypeParameterUnusedInFormals"})
<T> T doInitialize(@NonNull Class<? extends Initializer<?>> component) {
Object result;
synchronized (sLock) {
result = mInitialized.get(component);
if (result == null) {
result = doInitialize(component, new HashSet<Class<?>>());
}
}
return (T) result;
}
}
Tips
從上述的源碼解析可知,整個(gè)過(guò)程分為2部分
- 提取當(dāng)前
Provider的meta-data數(shù)據(jù),從meta-data上可以獲取對(duì)應(yīng)Initializer的類(lèi)信息 - 根據(jù)提取到
Initializer的類(lèi)信息,進(jìn)行反射調(diào)用構(gòu)造函數(shù)和調(diào)用onCreate方法;
因此可以不使用第一步進(jìn)行初始化,選擇合適時(shí)機(jī)進(jìn)行初始化,也就官網(wǎng)說(shuō)的延遲初始化, 此時(shí)調(diào)用上述
AppInitializer#initializeComponent進(jìn)行初始化
默認(rèn)行為是從 Provider 清單文件聲明 meta-data 提取類(lèi)信息,因此當(dāng)不需要某個(gè)初始化的時(shí)候,可以屏蔽對(duì)應(yīng) meta-data 的類(lèi)信息;或者使用 aapt2 所帶的 tools:node="remove" 進(jìn)行移除
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<!-- tools:node="remove" 在構(gòu)建的時(shí)候,不會(huì)打進(jìn)去 -->
<meta-data android:name="com.example.ExampleLoggerInitializer"
tools:node="remove" />
</provider>
同時(shí) tools:node="remove" 也適用于 provider 結(jié)點(diǎn),使整個(gè) provider 結(jié)點(diǎn)移除
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
tools:node="remove" />