Android組件初始化三種方式小結(jié)
方式一:使用 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)說(shuō),較為繁瑣);同時(shí)如果組件存在依賴關(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)用依賴該組件重復(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):如果組件間初始化是有依賴性,則多個(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<*>
,重寫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)前依賴的類庫(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
中聲明該組件所依賴組件,在初始該組件的時(shí)候會(huì)先初始化該組件所依賴組件 - 缺點(diǎn):從
Initializer#dependencies
方法參數(shù)可知,需要所依賴組件實(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 類信息的 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 記錄正在初始化的類,主要用于防止循環(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: 反射獲取此類,并檢查是否是 Initializer 的子類 Class<?> clazz = Class.forName(key); if (Initializer.class.isAssignableFrom(clazz)) { Class<? extends Initializer<?>> component = (Class<? extends Initializer<?>>) clazz; // step8: 記錄當(dāng)前類進(jìn)入發(fā)現(xiàn)集合中 mDiscovered.add(component); } } } // step9: 遍歷當(dāng)前發(fā)現(xiàn)的類,開(kāi)始初始化 for (Class<? extends Initializer<?>> component : mDiscovered) { // Tips: initializing 記錄正在初始化的類,這里為空集合 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)依賴 if (initializing.contains(component)) { String message = String.format( "Cannot initialize %s. Cycle detected.", component.getName() ); throw new IllegalStateException(message); } Object result; // step11: 檢查此類是否已初始化,已初始化這直接返回,反之進(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)前依賴組件未進(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
的類信息 - 根據(jù)提取到
Initializer
的類信息,進(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
提取類信息,因此當(dāng)不需要某個(gè)初始化的時(shí)候,可以屏蔽對(duì)應(yīng) meta-data
的類信息;或者使用 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" />
以上就是Android組件初始化三種方式小結(jié)的詳細(xì)內(nèi)容,更多關(guān)于Android組件初始化的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android BottomNavigationBar底部導(dǎo)航控制器使用方法詳解
這篇文章主要為大家詳細(xì)介紹了Android BottomNavigationBar底部導(dǎo)航控制器使用方法,感興趣的小伙伴們可以參考一下2016-03-03Android自定義控件案例匯總2(自定義開(kāi)關(guān)、下拉刷新、側(cè)滑菜單)
這篇文章主要介紹了Android自定義控件案例匯總,自定義開(kāi)關(guān)、Listview實(shí)現(xiàn)下拉刷新、側(cè)滑菜單,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-12-12Android 官推 kotlin-first 的圖片加載庫(kù)——Coil的使用入門
這篇文章主要介紹了Android 官推 kotlin-first 的圖片加載庫(kù)——Coil的使用入門,幫助大家更好的理解和學(xué)習(xí)使用Android,感興趣的朋友可以了解下2021-04-04Android Camera2采集攝像頭原始數(shù)據(jù)
這篇文章主要介紹了Android Camera2采集攝像頭原始數(shù)據(jù)并進(jìn)行手工預(yù)覽的功能實(shí)現(xiàn)原理以及代碼分析,需要的朋友學(xué)習(xí)下吧。2018-02-02Android項(xiàng)目實(shí)現(xiàn)視頻播放器
這篇文章主要為大家詳細(xì)介紹了Android項(xiàng)目實(shí)現(xiàn)視頻播放器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-03-03Android TelephonyManager詳解及實(shí)現(xiàn)代碼
本文主要介紹Android TelephonyManager, 這里整理了關(guān)于Android TelephoneManager的相關(guān)資料,并附有示例代碼和實(shí)現(xiàn)效果圖,有需要的朋友可以參考下2016-08-08微信小程序—微信跳一跳,Android游戲助手(外掛)使用教程詳解
這篇文章主要介紹了微信小程序—微信跳一跳,Android游戲助手(外掛)使用教程詳解,需要的朋友可以參考下2018-01-01android基本控件ToggleButton&Switch使用指南
本文給大家匯總介紹了android的2個(gè)基本控件ToggleButton和Switch的使用方法,非常的詳細(xì),有需要的小伙伴可以參考下。2016-01-01