最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Android組件初始化三種方式小結(jié)

 更新時(shí)間:2023年08月13日 09:13:07   作者:拉不拉了個(gè)多  
在Android中,組件的初始化通常需要在適當(dāng)?shù)纳芷诜椒ㄖ羞M(jìn)行,本文給大家詳細(xì)的介紹了初始化的三種方式,文中有詳細(xì)的代碼示例供大家參考,具有一定的參考價(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)單易用,只需在 主moduleApplication#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)行初始化,則避免組件使用方主moduleApplication#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í)行 ActivityThreadmain 方法,執(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)前 Providermeta-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)文章

  • adb無(wú)法連接雷電模擬器問(wèn)題的解決方式

    adb無(wú)法連接雷電模擬器問(wèn)題的解決方式

    雷電模擬器優(yōu)點(diǎn)是可設(shè)置的選項(xiàng)要比天天模擬器多,下面這篇文章主要給大家介紹了關(guān)于adb無(wú)法連接雷電模擬器問(wèn)題的解決方式,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-02-02
  • Android解決View的滑動(dòng)沖突的方法

    Android解決View的滑動(dòng)沖突的方法

    這篇文章主要介紹了Android解決View的滑動(dòng)沖突的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-07-07
  • Android CameraX結(jié)合LibYUV和GPUImage自定義相機(jī)濾鏡

    Android CameraX結(jié)合LibYUV和GPUImage自定義相機(jī)濾鏡

    之前使用Camera實(shí)現(xiàn)了一個(gè)自定義相機(jī)濾鏡(Android自定義相機(jī)濾鏡 ),但是運(yùn)行起來(lái)有點(diǎn)卡頓,這次用Camerax來(lái)實(shí)現(xiàn)一樣的效果發(fā)現(xiàn)很流暢,在此記錄一下,也希望能幫到有需要的同學(xué)
    2021-12-12
  • Android多線程斷點(diǎn)續(xù)傳下載實(shí)現(xiàn)代碼

    Android多線程斷點(diǎn)續(xù)傳下載實(shí)現(xiàn)代碼

    這篇文章主要介紹了Android多線程斷點(diǎn)續(xù)傳下載實(shí)現(xiàn)代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-11-11
  • Android中利用C++處理Bitmap對(duì)象的實(shí)現(xiàn)方法

    Android中利用C++處理Bitmap對(duì)象的實(shí)現(xiàn)方法

    下面小編就為大家?guī)?lái)一篇Android中利用C++處理Bitmap對(duì)象的實(shí)現(xiàn)方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-03-03
  • Android ViewPager動(dòng)態(tài)加載問(wèn)題

    Android ViewPager動(dòng)態(tài)加載問(wèn)題

    這篇文章主要介紹了Android ViewPager動(dòng)態(tài)加載問(wèn)題,需要的朋友可以參考下
    2017-03-03
  • 探尋Android的線程問(wèn)題

    探尋Android的線程問(wèn)題

    這篇文章主要介紹了探尋Android的線程問(wèn)題的相關(guān)資料,需要的朋友可以參考下
    2016-03-03
  • Android在代碼中設(shè)置沉浸式布局的方法

    Android在代碼中設(shè)置沉浸式布局的方法

    這篇文章主要介紹了Android在代碼中設(shè)置沉浸式布局的方法,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2017-03-03
  • Android自定義實(shí)現(xiàn)一個(gè)車牌字母選擇鍵盤

    Android自定義實(shí)現(xiàn)一個(gè)車牌字母選擇鍵盤

    這篇文章主要為大家詳細(xì)介紹了Android如何自定義實(shí)現(xiàn)一個(gè)車牌字母選擇鍵盤,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-06-06
  • Android編程簡(jiǎn)單獲取網(wǎng)絡(luò)上的圖片

    Android編程簡(jiǎn)單獲取網(wǎng)絡(luò)上的圖片

    這篇文章主要介紹了Android編程簡(jiǎn)單獲取網(wǎng)絡(luò)上的圖片,結(jié)合實(shí)例形式分析了Android獲取網(wǎng)絡(luò)圖片及加載顯示的相關(guān)操作步驟與注意事項(xiàng),需要的朋友可以參考下
    2016-10-10

最新評(píng)論

凤台县| 黄大仙区| 阜宁县| 观塘区| 宁陵县| 凤冈县| 凌云县| 广州市| 麻城市| 濉溪县| 东海县| 威宁| 平泉县| 岳普湖县| 岑巩县| 新乡县| 卫辉市| 辽阳县| 灵台县| 理塘县| 泰和县| 达尔| 许昌市| 三门县| 本溪市| 兴仁县| 西安市| 安福县| 赫章县| 屯门区| 烟台市| 白水县| 苍山县| 璧山县| 沁源县| 公安县| 临夏市| 南和县| 铁岭县| 盐城市| 扬中市|