Android Jetpack 之 StartUp真的是太香了


Jetpack StartUp官網(wǎng) 需要翻墻

StartUp的作用

如果一個(gè)app依賴了很多需要初始化的sdk, 對(duì)于開(kāi)發(fā)者來(lái)說(shuō),都是放在application啟動(dòng)的時(shí)候oncreate里面去初始化,這樣是不太友好的。而StartUp就能解決這個(gè)問(wèn)題。多人協(xié)作開(kāi)發(fā)的時(shí)候,都需要去修改application里面的代碼,這個(gè)是比較容易出問(wèn)題的。 所以startUp這個(gè)庫(kù),有一種模塊化的思想在里面。

StartUp的原理

借助于ContentProvider.
相信做android開(kāi)發(fā)的朋友,都應(yīng)該知道App在啟動(dòng)的時(shí)候會(huì)去解析清單文章里面注冊(cè)的ContentProvider,
然后調(diào)用了ContentProvider里面的onCreate生命周期方法, 因此, 整個(gè)app的初始化代碼的入口就 可以
放在contentProvicer的onCreate方法里面執(zhí)行。

ContentProvider需要我們自己去寫(xiě)嗎

ContentProvider當(dāng)然不需要我們自己去寫(xiě),這個(gè)已經(jīng)在SDK里面已經(jīng)提供好了。
只需要引入SDK即可

dependencies {
    implementation "androidx.startup:startup-runtime:1.1.1"
}

注冊(cè)ContentProvider

ContentProvider是屬于四大組件之一, 因此必須要注冊(cè)到清單文件里面。
而注冊(cè)到清單文件里面的代碼,只需要根據(jù)官網(wǎng)提供的文檔注冊(cè)即可。對(duì)于
下面的代碼,對(duì)于開(kāi)發(fā)者而言,只需要修改meta-data 里面的類的路徑,其它
的代碼都是不需要改動(dòng)的。

<provider
    android:name="androidx.startup.InitializationProvider"
    android:authorities="${applicationId}.androidx-startup"
    android:exported="false"
    tools:node="merge">
    <!-- This entry makes ExampleLoggerInitializer discoverable. -->
    <meta-data  android:name="com.example.ExampleLoggerInitializer"
          android:value="androidx.startup" />
</provider>

meta-data具體有什么用

前面已經(jīng)說(shuō)過(guò),ContentProvider啟動(dòng)的時(shí)候,就會(huì)去執(zhí)行初始化代碼。
而這初始化代碼哪里來(lái)? 就是根據(jù)metaData里面提供的類的路徑,反射
創(chuàng)建出實(shí)例,從而執(zhí)行對(duì)應(yīng)的方法。

而meta-data里面配置的類并不是任意一個(gè)類都是可以的, 而是要實(shí)現(xiàn)SDK
里面提供的一個(gè)接口。 并重寫(xiě)對(duì)應(yīng)的兩個(gè)方法

public interface Initializer<T> {

  @NonNull
  T create(@NonNull Context context);

  @NonNull
  List<Class<? extends Initializer<?>>> dependencies();
}

接口方法介紹

接口需要傳入一個(gè)泛型, 而create方法需要返回泛型的實(shí)例。
首先看一下 create()方法的作用: 初始化的代碼 就是在create方法里面執(zhí)行的。

需要一個(gè)泛型, 那就創(chuàng)建一個(gè)類,這個(gè)類的名字是可以隨便定義的,比如我創(chuàng)建
一個(gè)UmengInit的類,這個(gè)類對(duì)外暴露一個(gè)init方法。而這個(gè)UmengInit類,才是真正的
代碼初始化的類,而暴露的這個(gè)init方法就是需要在上面的oncreate方法里執(zhí)行的。
代碼如下:

class UmengInit {
    fun init(application: Context?) {
        umengInit(application)
        pushInit(application)
    }

    private fun umengInit(application: Context?) {
        val umengKey = ResourceUtils.getMetaDateFromName("UMENG_APPKEY")
        val umengMessageScret = ResourceUtils.getMetaDateFromName("UMENG_MESSAGE_SCRET")
        val channel = PackerNg.getChannel(application)
        UMConfigure.init(
            application,
            umengKey,
            channel,
            UMConfigure.DEVICE_TYPE_PHONE,
            umengMessageScret
        )
        UmengConfig.setId()
    }

    private fun pushInit(application: Context?) {
        val push = UmengPushAdapter()
        push?.init(application)
    }
}

在onCreate方法里面如何使用UmengInit類

創(chuàng)建一個(gè)類,必須要實(shí)現(xiàn)上面的Initializer<T>接口, 這里的泛型就是UmengInit
而此時(shí)創(chuàng)建的這個(gè)類就是需要添加到清單文件meta-data里面
只需要把name改成剛創(chuàng)建的類的全路徑即可。
比如我創(chuàng)建的這個(gè)類的代碼如下:

public class UmengInitial implements Initializer<UmengInit> {
    @NonNull
    @Override
    public UmengInit create(@NonNull Context context) {
        UmengInit init = new UmengInit();
        init.init(context);
        return init;
    }

    @NonNull
    @Override
    public List<Class<? extends Initializer<?>>> dependencies() {
        List<Class<? extends Initializer<?>>> list = new ArrayList();
        list.add(FirstInitial.class);
        return list;
    }
}
image.png

結(jié)束語(yǔ)

寫(xiě)到這里面,可以說(shuō)startUp的初始化的脈絡(luò)已經(jīng)講得差不多了,這個(gè)時(shí)候運(yùn)行代碼,這個(gè)初始化
代碼是肯定可以的執(zhí)行的。

你確定你講清楚了嗎? dependencies()方法不都還沒(méi)講嗎? 哦!?。?頭腦暈了,確實(shí)沒(méi)講。

dependencies()方法又有何用呢

上面的UmengInitial只是負(fù)責(zé)初始化umenng, 功能是單一的,
這個(gè)時(shí)候如果有一個(gè)圖片初始化,比如叫: ImageInitial.
如何在功能上需要 ImageInitial的初始化代碼要先于UmengInitial的初始化代碼,那該如何實(shí)現(xiàn)呢?

有兩種方案:

第一種方案,用到清單文章里面meta-data注冊(cè)的先后順序
第二種方案,用到dependencies()方法

第一種方案, ImageInitial寫(xiě)在前面就能保證先執(zhí)行

 <provider
                android:authorities="${applicationId}.androidx-startup"
                android:name="androidx.startup.InitializationProvider"
                android:exported="false"
                tools:node="merge">
            <meta-data
                    android:name="com.micker.aqhy.application.initial.ImageInitial"
                    android:value="@string/androidx_startup"/>
            <meta-data
                    android:name="com.micker.aqhy.application.initial.UmengInitial"
                    android:value="@string/androidx_startup"/>
        </provider>

第二種方案

在dependencies()方法里面把ImageInitial添加進(jìn)去
在這個(gè)方法里面關(guān)聯(lián)的Initializer,是要優(yōu)先執(zhí)行的。
而且這樣寫(xiě)了之后,在清單文件里面就不用注冊(cè)ImageInitial, 只需要注冊(cè)UmengInitial就行

public class UmengInitial implements Initializer<UmengInit> {
    @NonNull
    @Override
    public UmengInit create(@NonNull Context context) {
        UmengInit init = new UmengInit();
        init.init(context);
        return init;
    }

    @NonNull
    @Override
    public List<Class<? extends Initializer<?>>> dependencies() {
        List<Class<? extends Initializer<?>>> list = new ArrayList();
        list.add(ImageInitial.class);
        return list;
    }
}

最后我把源碼給貼上來(lái), 源碼很簡(jiǎn)單, 相信你都能看明白

 void discoverAndInitialize() {
        try {
            Trace.beginSection(SECTION_NAME);
            ComponentName provider = new ComponentName(mContext.getPackageName(),
                    InitializationProvider.class.getName());
            ProviderInfo providerInfo = mContext.getPackageManager()
                    .getProviderInfo(provider, GET_META_DATA);
            Bundle metadata = providerInfo.metaData;
            String startup = mContext.getString(R.string.androidx_startup);
            if (metadata != null) {
                Set<Class<?>> initializing = new HashSet<>();
                Set<String> keys = metadata.keySet();
                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));
                            }
                            doInitialize(component, initializing);
                        }
                    }
                }
            }
        } catch (PackageManager.NameNotFoundException | ClassNotFoundException exception) {
            throw new StartupException(exception);
        } finally {
            Trace.endSection();
        }
    }
 <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;
                if (!mInitialized.containsKey(component)) {
                    initializing.add(component);
                    try {
                        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) {
                                if (!mInitialized.containsKey(clazz)) {
                                    doInitialize(clazz, initializing);
                                }
                            }
                        }
                        if (StartupLogger.DEBUG) {
                            StartupLogger.i(String.format("Initializing %s", component.getName()));
                        }
                        result = initializer.create(mContext);
                        if (StartupLogger.DEBUG) {
                            StartupLogger.i(String.format("Initialized %s", component.getName()));
                        }
                        initializing.remove(component);
                        mInitialized.put(component, result);
                    } catch (Throwable throwable) {
                        throw new StartupException(throwable);
                    }
                } else {
                    result = mInitialized.get(component);
                }
                return (T) result;
            } finally {
                Trace.endSection();
            }
        }
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容