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;
}
}

結(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();
}
}
}