和Jayce一起學(xué)習(xí)Jetpack -- 開篇
Google 官方解釋
和Jayce一起學(xué)習(xí)Jetpack -- startup的用法
這次解析我們從 定義在 manifest中的 InitializationProvider 入手
@RestrictTo(RestrictTo.Scope.LIBRARY)
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;
}
@Nullable
@Override
public Cursor query(
@NonNull Uri uri,
@Nullable String[] projection,
@Nullable String selection,
@Nullable String[] selectionArgs,
@Nullable String sortOrder) {
throw new IllegalStateException("Not allowed.");
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
throw new IllegalStateException("Not allowed.");
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
throw new IllegalStateException("Not allowed.");
}
@Override
public int delete(
@NonNull Uri uri,
@Nullable String selection,
@Nullable String[] selectionArgs) {
throw new IllegalStateException("Not allowed.");
}
@Override
public int update(
@NonNull Uri uri,
@Nullable ContentValues values,
@Nullable String selection,
@Nullable String[] selectionArgs) {
throw new IllegalStateException("Not allowed.");
}
}
這個provider 也很簡單 關(guān)鍵的代碼都在 AppInitializer.getInstance(context).discoverAndInitialize(); 這一句。
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;
mDiscovered.add(component);
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();
}
}
- 這個方法,首先第一步 獲取定義在 provider 里面的 metadata。
- 第二步就是便利 metadata 獲取所有value為
R.string.androidx_startup
的key值狮荔。 - 第三步 通過name 獲取class
- 執(zhí)行
doInitialize(component, initializing);
<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();
}
}
}
從這個方法中可以看到這里面的邏輯是
- 判斷是否有依賴,如果有首先創(chuàng)建依賴介粘,并且調(diào)用create
- 如果沒有則創(chuàng)建當前的實例,并且調(diào)用create
- 判斷去重晚树,避免重復(fù)創(chuàng)建姻采。
不自動加載組件的邏輯
AppInitializer.getInstance(context)
.initializeComponent(ExampleLoggerInitializer.class);
######
public <T> T initializeComponent(@NonNull Class<? extends Initializer<T>> component) {
return doInitialize(component, new HashSet<Class<?>>());
}
這里的邏輯就是不自動沖xml中讀取自動創(chuàng)建, 改為手動調(diào)用doInitialize
爵憎,內(nèi)部邏輯是一樣慨亲。
是不是很簡單。
總結(jié)
結(jié)合content provider的特性宝鼓,我們很容易可以知道 這些創(chuàng)建的組件調(diào)用u會在 application 的oncreatae之前調(diào)用刑棵,但是content provider的oncreate 也是在主線程調(diào)用,還是會占用主線程的資源愚铡。
綜合來說蛉签,startup還是以解偶 啟動流程,規(guī)范化啟動流程為主沥寥。而獨立模塊化的啟動組件碍舍,也方便各個擊破地提高我們的啟動速度。