AppStartUp 进阶使用
手动初始化
上面我们讲解了 AppStartUp 的基本使用步骤,如果我们不像在 Application onCreate 之前执行我们的 ExampleLoggerInitializer,要怎么使用呢?
其实很简单,
第一步,在 AndroidManifest InitializationProvider 中移除 移除 <meta-data 标签
在代码中调用 AppInitializer initializeComponent 方法初始化
<provider android:name="androidx.startup.InitializationProvider" android:authorities="${applicationId}.androidx-startup" android:exported="false" tools:node="merge"> </provider>
AppInitializer.getInstance(context).initializeComponent(ExampleLoggerInitializer::class.java)
App start up 源码分析
我们首先来看一下他的结构,只有简单的几个类
Initializer 这个接口就没有必要说了,很简单,只有两个方法。
InitializationProvider 继承了 ContentProvider,借助了 ContentProvider 会在 Application onCreate 之前执行的特点。来执行一些初始化操作。
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; } ---- }
我们可以看到在 onCreate 方法中调用 AppInitializer discoverAndInitialize 方法进行初始化。
- 找到 AndroidManifest InitializationProvider 下的 meta 便签
- 判断 meta 便签下 value 的值是不是 androidx.startup
- 判断是不是实现 Initializer 接口,是的话,执行 doInitialize 方法
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); // 找到 metadata 标签 if (metadata != null) { Set<Class<?>> initializing = new HashSet<>(); Set<String> keys = metadata.keySet(); for (String key : keys) { String value = metadata.getString(key, null); // 判断 value 的值是不是 androidx.startup // 判断是不是实现了 Initializer 接口,是的话,反射初始化 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(); } }
doInitialize 方法
<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(); } } }
可以看到在执行初始化的时候,先判断了是否有依赖项,有的话先执行依赖项的初始化
小结
- App start up,我觉得他的设计初衷应该是为了收拢 ContentProvider,实际上对启动优化的帮助不是很大。
- 如果你的项目都是同步初始化的话,并且使用到了多个ContentProvider,App Startup可能有一定的优化空间,毕竟统一到了一个ContentProvider中,同时支持了简单的顺序依赖。
- ContentProvider 初始化的这个思想,目前有挺多 SDK 这么做的,像 FaceBook 广告 SDK,友盟 SDk 等。我们在启动优化的时候,是不是可以去掉相应的 ContentProvider,减少创建 Provider 的时间
- 实际项目中 启动优化,大多数啊都会使用多线程异步加载,这时候 App start up 就显得很鸡肋了,没用
参考博客: Jetpack新成员,App Startup一篇就懂
本文收录于 github.com/gdutxiaoxu/… 「Android学习+面试指南」一份涵盖大部分 Android 程序员所需要掌握的核心知识。准备 Android 面试,首选 AndroidGuide!