Android架構(gòu)進(jìn)階之深入理解AppStartup原理
前言
Android Startup提供一種在應(yīng)用啟動(dòng)時(shí)能夠更加簡(jiǎn)單、高效的方式來(lái)初始化組件。開(kāi)發(fā)人員可以使用Android Startup來(lái)簡(jiǎn)化啟動(dòng)序列,并顯式地設(shè)置初始化順序與組件之間的依賴(lài)關(guān)系;
今天我們就來(lái)聊聊
一、使用步驟簡(jiǎn)單介紹
使用 AndroidX App Startup 來(lái)運(yùn)行所有依賴(lài)項(xiàng)的初始化有兩種方式:
自動(dòng)初始化;
手動(dòng)初始化(也是延遲初始化);
1、自動(dòng)初始化
在 build.gradle 文件內(nèi)添加依賴(lài);
- implementation "androidx.startup:startup-runtime:1.0.0-alpha01"
實(shí)現(xiàn) Initializer 接口,并重寫(xiě)兩個(gè)方法,來(lái)初始化組件;
- public class MvpInitializer implements Initializer<Void> {
- @NonNull
- @Override
- public Void create(@NonNull Context context) {
- MvpManager.init(context);
- return null;
- }
- @NonNull
- @Override
- public List<Class<? extends Initializer<?>>> dependencies() {
- return new ArrayList<>();
- }
- }
- ......
- }
create(Context): 這里進(jìn)行組件初始化工作;
dependencies(): 返回需要初始化的列表,同時(shí)設(shè)置 App 啟動(dòng)時(shí)依賴(lài)庫(kù)運(yùn)行的順序;
在 AndroidManifest.xml 文件中注冊(cè) InitializationProvider;
- <application>
- <provider
- android:authorities="${applicationId}.androidx-startup"
- android:name="androidx.startup.InitializationProvider"
- android:exported="false"
- tools:node="merge" >
- <!-- 自動(dòng)初始化 -->
- <meta-data android:name="com.test.Initializer" android:value="androidx.startup"/>
- </provider>
- </application>
App 啟動(dòng)的時(shí) App Startup 會(huì)讀取 AndroidManifest.xml 文件里面的 InitializationProvider 下面的
2、手動(dòng)初始化(也是延遲初始化)
在 build.gradle 文件內(nèi)添加依賴(lài);
創(chuàng)建一個(gè)類(lèi) LibaryD 實(shí)現(xiàn) Initializer 接口,并重寫(xiě)兩個(gè)方法,來(lái)初始化組件;
在 AndroidManifest.xml 文件中注冊(cè) InitializationProvider
- <application>
- <provider
- android:name="androidx.startup.InitializationProvider"
- android:authorities="${applicationId}.androidx-startup"
- android:exported="false"
- tools:node="merge">
- <!-- 手動(dòng)初始化(也是延遲初始化) -->
- <meta-data
- android:name="com.test.Initializer"
- android:value="androidx.startup"
- tools:node="remove" />
- </provider>
- </application>
- 只需要在
標(biāo)簽內(nèi)添加 tools:node="remove" 清單合并工具會(huì)將它從清單文件中刪除; - 在需要的地方進(jìn)行初始化,調(diào)用以下代碼進(jìn)行初始化;
- Initializer.getInstance(context).initializeComponent(Initializer::class.java);
- 如果組件初始化之后,再次調(diào)用 AppInitializer.initializeComponent() 方法不會(huì)再次初始化;
- 手動(dòng)初始化(也是延遲初始化)是非常有用的,組件不需要在 App 啟動(dòng)時(shí)運(yùn)行,只需要在需要它地方運(yùn)行,可以減少 App 的啟動(dòng)時(shí)間,提高啟動(dòng)速度;
二、源碼分析
1、InitializationProvider
在AndroidManifest文件中配置的組件名必須為androidx.startup.InitializationProvider,現(xiàn)在我們來(lái)看這個(gè)類(lèi)的源碼;
- InitializationProvider.java
- 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;
- }
- @Override
- public Cursor query(...) {
- throw new IllegalStateException("Not allowed.");
- }
- @Override
- public String getType(...) {
- throw new IllegalStateException("Not allowed.");
- }
- @Nullable
- @Override
- public Uri insert(...) {
- throw new IllegalStateException("Not allowed.");
- }
- @Override
- public int delete(...) {
- throw new IllegalStateException("Not allowed.");
- }
- @Override
- public int update(...) {
- throw new IllegalStateException("Not allowed.");
- }
- }
InitializationProvider其實(shí)也是利用了 ContentProvider 的啟動(dòng)機(jī)制,在ContentProvider#onCreate(...)中執(zhí)行初始化;
ContentProvider 的其他方法是沒(méi)有意義的,所以都拋出了IllegalStateException;
2、自動(dòng)初始化分析
App Startup 在 ContentProvider 中調(diào)用了AppInitializer#discoverAndInitialize()執(zhí)行自動(dòng)初始化;
AppInitializer是 App StartUp 框架的核心類(lèi),整個(gè) App Startup 框架的代碼其實(shí)非常少,其中很大部分核心代碼都在 AppInitializer 類(lèi)中;
2.1.AppInitializer.java discoverAndInitialize
- final Set<Class<? extends Initializer<?>>> mDiscovered;
- void discoverAndInitialize() {
- 獲取 androidx.startup.InitializationProvider 組件信息
- ComponentName provider = new ComponentName(mContext.getPackageName(), InitializationProvider.class.getName());
- ProviderInfo providerInfo = mContext.getPackageManager().getProviderInfo(provider, GET_META_DATA);
- androidx.startup 字符串
- String startup = mContext.getString(R.string.androidx_startup);
- 獲取組件信息中的 meta-data 數(shù)據(jù)
- Bundle metadata = providerInfo.metaData;
- 遍歷 meta-data 數(shù)據(jù)
- if (metadata != null) {
- Set<Class<?>> initializing = new HashSet<>();
- Set<String> keys = metadata.keySet();
- for (String key : keys) {
- String value = metadata.getString(key, null);
- 判斷 meta-data 數(shù)據(jù)中,value 為 androidx.startup 的鍵值對(duì)
- if (startup.equals(value)) {
- Class<?> clazz = Class.forName(key);
- 檢查指定的類(lèi)是 Initializer 接口的實(shí)現(xiàn)類(lèi)
- if (Initializer.class.isAssignableFrom(clazz)) {
- Class<? extends Initializer<?>> component = (Class<? extends Initializer<?>>) clazz;
- 將 Class 添加到 mDiscovered Set 中
- mDiscovered.add(component);
- 初始化此組件
- doInitialize(component, initializing);
- }
- }
- }
- }
- }
- mDiscovered 用于判斷組件是否已經(jīng)自動(dòng)啟動(dòng)
- public boolean isEagerlyInitialized(@NonNull Class<? extends Initializer<?>> component) {
- return mDiscovered.contains(component);
- }
- 獲取androidx.startup.InitializationProvider組件信息(在各個(gè) Module 中聲明的組件信息,會(huì)在manifest merger tool的處理下合并);
- androidx.startup字符串;
- 獲取組件信息中的 meta-data 數(shù)據(jù);
- 遍歷 meta-data 數(shù)據(jù);
- 判斷 meta-data 數(shù)據(jù)中,value 為 androidx.startup 的鍵值對(duì);
- 檢查指定的類(lèi)是 Initializer 接口的實(shí)現(xiàn)類(lèi);
- 將 Class 添加到 mDiscovered Set 中,這將用于后續(xù) 判斷組件是否已經(jīng)自動(dòng)啟動(dòng);
- 初始化此組件;
2.2.AppInitializer.java AppInitializer.java
- private static final Object sLock = new Object();
- 緩存每個(gè)組件的初始化結(jié)果;
- final Map<Class<?>, Object> mInitialized;
- 初始化此組件
- <T> T doInitialize(Class<? extends Initializer<?>> component, Set<Class<?>> initializing) {
- 對(duì) sLock 加鎖
- Object result;
- 判斷 initializing 中存在當(dāng)前組件,說(shuō)明存在循環(huán)依賴(lài)
- if (initializing.contains(component)) {
- String message = String.format("Cannot initialize %s. Cycle detected.", component.getName());
- throw new IllegalStateException(message);
- }
- 檢查當(dāng)前組件是否已初始化
- if (!mInitialized.containsKey(component)) {
- 當(dāng)前組件未初始化
- 記錄正在初始化
- initializing.add(component);
- 通過(guò)反射實(shí)例化 Initializer 接口實(shí)現(xiàn)類(lèi)
- Object instance = component.getDeclaredConstructor().newInstance();
- Initializer<?> initializer = (Initializer<?>) instance;
- 遍歷所依賴(lài)的組件
- List<Class<? extends Initializer<?>>> dependencies = initializer.dependencies();
- if (!dependencies.isEmpty()) {
- for (Class<? extends Initializer<?>> clazz : dependencies) {
- 如果所依賴(lài)的組件未初始化,遞歸執(zhí)行初始化
- if (!mInitialized.containsKey(clazz)) {
- doInitialize(clazz, initializing); 注意:這里將 initializing 作為參數(shù)傳入
- }
- }
- }
- 初始化當(dāng)前組件
- result = initializer.create(mContext);
- 移除正在初始化記錄
- initializing.remove(component);
- 緩存初始化結(jié)果
- mInitialized.put(component, result);
- } else {
- 當(dāng)前組件已經(jīng)初始化,直接返回
- result = mInitialized.get(component);
- }
- return (T) result;
- }
- 對(duì) sLock 加鎖;
- 判斷 initializing 中存在當(dāng)前組件,說(shuō)明存在循環(huán)依賴(lài)(這是因?yàn)檫f歸初始化所依賴(lài)的組件時(shí),會(huì)將 initializing 作為參數(shù)傳入,如果 initializing 中存在當(dāng)前組件,說(shuō)明依賴(lài)關(guān)系形成回環(huán),如果不拋出異常,將形成無(wú)限遞歸);
- 檢查當(dāng)前組件是否已初始化;
- 記錄正在初始化;
- 通過(guò)反射實(shí)例化 Initializer 接口實(shí)現(xiàn)類(lèi);
- 遍歷所依賴(lài)的組件,如果所依賴(lài)的組件未初始化,遞歸調(diào)用doInitialize(...)執(zhí)行初始化;
- 初始化當(dāng)前組件;
- 移除正在初始化記錄;
- 緩存初始化結(jié)果;
3、手動(dòng)初始化源碼分析
手動(dòng)初始化(懶加載)的源碼分析:
- AppInitializer.java
- public <T> T initializeComponent(@NonNull Class<? extends Initializer<T>> component) {
- 調(diào)用 doInitialize(...) 方法:
- return doInitialize(component, new HashSet<Class<?>>());
- }
其實(shí)非常簡(jiǎn)單,就是調(diào)用上一節(jié)的doInitialize(...)執(zhí)行初始化。需要注意的是,這個(gè)方法是允許在子線程調(diào)用的,換句話說(shuō),自動(dòng)初始化與手動(dòng)初始化是存在線程同步問(wèn)題的,那么 App Startup 是如何解決的呢?
前面有一個(gè)sLock沒(méi)有說(shuō)嗎?其實(shí)它就是用來(lái)保證線程同步的鎖:
- <T> T doInitialize(Class<? extends Initializer<?>> component, Set<Class<?>> initializing) {
- 對(duì) sLock 加鎖
- synchronized (sLock) {
- ...
- }
- }
總結(jié)
App Startup 是 Jetpack 的新成員,是為了解決因 App 啟動(dòng)時(shí)運(yùn)行多個(gè) ContentProvider 會(huì)增加 App 的啟動(dòng)時(shí)間的問(wèn)題;
使用了一個(gè) InitializationProvider 管理多個(gè)依賴(lài)項(xiàng),消除了每個(gè)庫(kù)單獨(dú)使用 ContentProvider 成本,減少初始化時(shí)間;
App Startup 允許你自定義組件初始化順序;
App Startup 提供了一種延遲初始化組件的方法,減少 App 初始化時(shí)間;