StartUp
1. Application初始化
startup主要作用是优化app启动流程,实现更优雅的初始化操作。通常我们使用第三方组件的时候都需要在Application中进行初始化,比如:
class App : BaseApp(){
override fun onCreate() {
super.onCreate()
A.init()
B.init(this,"xx")
XXX.init(this)
}
}
如果在application中初始化的组件多,就会消耗更多的时间。所以Jetpack引入了新的组件StartUp
2. StartUp的使用
首先需要引入依赖:
dependencies {
implementation "androidx.startup:startup-runtime:1.1.0"
}
自己实现Initializer接口,这个接口很简单就两个方法:
class AppInit : Initializer<Unit> {
override fun create(context: Context) {
//init...
}
override fun dependencies(): List<Class<out Initializer<*>>> = emptyList()
}
- onCreat方法中执行初始化操作
- dependencies主要实现依赖其他Initializer,如果有其他Initializer,需先执行其他Initializer,再执行本Initializer。
然后在AndroidMainfest.xml中加入如下:
<application>
...
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<meta-data android:name="com.example.baselibrary.AppInit"
android:value="androidx.startup" />
</provider>
</application>
除了android:name="com.example.baselibrary.AppInit"需要改成自定义的 Initializer名称外,其他都不需要做啥改变
总结就是三步,非常简单:
- 引入App Startup的库。
- 自定义一个用于初始化的Initializer。
- 将自定义Initializer配置到AndroidManifest.xml当中。
如果不需要初始化,可以自行定义:
<!--标记为不初始化-->
<meta-data
android:name="com.example.baselibrary.AppInit"
tools:node="remove" />
然后在需要的时候动态执行:
AppInitializer.getInstance(this).initializeComponent(AppInit::class.java)
3. StartUp启动流程
我们知道app启动由AMS负责,由ActivityThread来创建并启动application,在application执行onCreat之前还有其它组件,比如contentProvider会执行初始化,startup就是在contentprovider中进行初始化的。
application与contentprovider初始化顺序:
Application 构造方法 –> Application.attachBaseContext –> ContentProvider 构造方法 –> ContentProvider.onCreate –> Application.onCreate –> Activity 构造方法 –> Activity attachBaseContext –> Activity.onCreate
startup在contentprovider的onCreat方法中初始化:
public class InitializationProvider extends ContentProvider {
@Override
public final boolean onCreate() {
Context context = getContext();
if (context != null) {
AppInitializer.getInstance(context).discoverAndInitialize();
} else {
throw new StartupException("Context cannot be null");
}
return true;
}
....
}
discoverAndInitialize就是执行Initializer初始化的方法:
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();
}
}
doInitialize()是最终执行方法:
@SuppressWarnings({"unchecked", "TypeParameterUnusedInFormals"})
<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()));
}
//回调creat方法
result = initializer.create(mContext);
if (StartupLogger.DEBUG) {
StartupLogger.i(String.format("Initialized %s", component.getName()));
}
initializing.remove(component);
//使用map保存
mInitialized.put(component, result);
} catch (Throwable throwable) {
throw new StartupException(throwable);
}
} else {
result = mInitialized.get(component);
}
return (T) result;
} finally {
Trace.endSection();
}
}
}
我们发现调用doInitialize的地方除了discoverAndInitialize还有一个initializeComponent,这个方法并没有被调用,是干啥的?其实是我们可以手动调用它,延迟startup的初始化。
@SuppressWarnings("unused")
public <T> T initializeComponent(@NonNull Class<? extends Initializer<T>> component) {
return doInitialize(component, new HashSet<Class<?>>());
}
4. 完结撒花
至此,Startup的介绍也就完成了,对于其他单独模块或者第三方引入词库还是非常不错的,可以做到无感知的初始化。
参考项目:mvvm_develop
之前的文章介绍了网络封装、组件化、基础工具等,有兴趣的可以查看:
- 写一个MVVM快速开发框架(一)基础类封装
- 写一个MVVM快速开发框架(二)组件化改造
- 写一个MVVM快速开发框架(三)单Activity+多Fragment模式
- 写一个MVVM快速开发框架(四)优雅的数据处理 - 掘金 (juejin.cn)
还有一些关于UI的基础工具: