一、前言
最近jetpack又有新成员了–App Startup。刚开始看到这个字面理解就是和app启动相关的。我们来看一下官方给出的解释:
看不懂?没关系,大致意思就是:提供了在 App 启动时初始化组件简单、高效的方法,无论是 library 开发人员还是 App 开发人员都可以使用 App Startup 显示的设置初始化顺序,简单的说就是 App Startup 提供了一个 ContentProvider 来运行所有依赖项的初始化,避免每个第三方库单独使用 ContentProvider 进行初始化,从而提高了应用的程序的启动速度。
二、WorkManagerInitializer
App 启动运行时会初始化一些逻辑,它们为了方便开发者使用,避免开发者手动调用,使用 ContentProvider 进行初始化,官方还提供了WorkManager的启动方式的示例,说明了它是如何通过WorkManagerInitializer 启动的。在AndroidManifest.xml文件中添加如下配置:
我们载来看看WorkManagerInitializer的源码
public class WorkManagerInitializer extends ContentProvider {
@Override
public boolean onCreate() {
// Initialize WorkManager with the default configuration.
WorkManager.initialize(getContext(), new Configuration.Builder().build());
return true;
}
....
}
关键方法就是onCreate()方法。
- 可以看到当前类继承了ContentProvider ,都知道它是android跨进程通信的一种方式
- 在onCreate()中,进行了WorkManager的初始化操作。
知道了以上两点,我们自己也可以模仿WorkManagerInitializer ,看看具体操作。
三、初体验
1、自定义 Lib1
代码如下:
public class Lib1 extends ContentProvider {
@Override
public boolean onCreate() {
Log.e("Lib1初始化》》》》》》》","1111111111111");
return true;
}
}
2、自定义 Lib2
代码如下:
public class Lib2 extends ContentProvider {
@Override
public boolean onCreate() {
Log.e("Lib2初始化》》》》》》》","2222222222222");
return true;
}
}
3、AndroidMenifest.xml配置
<provider
android:name=".Lib1"
android:authorities="${applicationId}.provider"
android:exported="false" />
<provider
android:name=".Lib2"
android:authorities="${applicationId}.provider2"
android:exported="false" />
4、结果
启动app,运行结果如下:
那么,如果我现在有这样一个需求:Lib1要依赖于Lib2。也就是说,只有当Lib2启动初始化完成以后,才初始化Lib,那该如何操作呢?,当然,Android Startup就是为此而存在的。
四、Android Startup
为了解决上面的问题,Startup为我们提供了Initializer这个接口,源码如下:
public interface Initializer<T> {
/**
* Initializes and a component given the application {@link Context}
*
* @param context The application context.
*/
@NonNull
T create(@NonNull Context context);
/**
* @return A list of dependencies that this {@link Initializer} depends on. This is
* used to determine initialization order of {@link Initializer}s.
* <br/>
* For e.g. if a {@link Initializer} `B` defines another
* {@link Initializer} `A` as its dependency, then `A` gets initialized before `B`.
*/
@NonNull
List<Class<? extends Initializer<?>>> dependencies();
}
- onCreate()方法中就是需要我们需要初始化的地方
- dependencies()方法,添加我们需要依赖的启动项。比如A需要依赖B,那么就可以在该方法中进行配置B。
1、InitializerLib1
public class InitializerLib1 implements Initializer<Lib1> {
@NonNull
@Override
public Lib1 create(@NonNull Context context) {
Lib1 lib1 = new Lib1();
lib1.onCreate();
return lib1;
}
@NonNull
@Override
public List<Class<? extends Initializer<?>>> dependencies() {
List<Class<? extends Initializer<?>>> dependencies = new ArrayList<>();
dependencies.add(InitializerLib2.class);
return dependencies;
}
}
它的启动需要依赖Lib2的启动。
2、InitializerLib2
public class InitializerLib2 implements Initializer<Lib2> {
@NonNull
@Override
public Lib2 create(@NonNull Context context) {
Lib2 lib2 = new Lib2();
lib2.onCreate();
return lib2;
}
@NonNull
@Override
public List<Class<? extends Initializer<?>>> dependencies() {
return Collections.emptyList();
}
}
它不需要依赖,在dependencies()返回空的list集合。
3、AndroidManifest.xml配置
<provider
android:authorities="${applicationId}.androidx-startup"
android:name="androidx.startup.InitializationProvider"
android:exported="false"
tools:node="merge">
<meta-data
android:name="com.yinggu.android.nutrition.startupdemo.InitializerLib1"
android:value="@string/androidx_startup"/>
</provider>
我们只配置了InitializerLib1,因为我们在它的dependencies()方法中依赖了InitializerLib2,所以就不需要配置了。看到这里,我们还是要看一下InitializationProvider
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;
}
...
}
只需要关注AppInitializer.getInstance(context).discoverAndInitialize()。
(1)discoverAndInitialize()
void discoverAndInitialize() {
try {
Trace.beginSection(SECTION_NAME);
ComponentName provider = new ComponentName(mContext.getPackageName(),
InitializationProvider.class.getName());
//1、找到meta-data的配置项
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)) {
//2、得到Initializer组件
Class<? extends Initializer<?>> component =
(Class<? extends Initializer<?>>) clazz;
if (StartupLogger.DEBUG) {
StartupLogger.i(String.format("Discovered %s", key));
}
// 3、开始初始化操作
doInitialize(component, initializing);
}
}
}
}
} catch (PackageManager.NameNotFoundException | ClassNotFoundException exception) {
throw new StartupException(exception);
} finally {
Trace.endSection();
}
}
以上代码,大致分为三个步骤:
- 第一步:找到meta-data的配置项
- 第二步:得到Initializer组件,这里对应InitializerLib1
- 第二步:开始初始化组件
(2)doInitialize()
<T> T doInitialize(
@NonNull Class<? extends Initializer<?>> component,
@NonNull Set<Class<?>> initializing) {
synchronized (sLock) {
...
//1、实例化该组件
Object instance = component.getDeclaredConstructor().newInstance();
Initializer<?> initializer = (Initializer<?>) instance;
//2、得到当前组件下的所有依赖组件
List<Class<? extends Initializer<?>>> dependencies =
initializer.dependencies();
if (!dependencies.isEmpty()) {
//3、递归依赖的组件
for (Class<? extends Initializer<?>> clazz : dependencies) {
if (!mInitialized.containsKey(clazz)) {
doInitialize(clazz, initializing);
}
}
}
if (StartupLogger.DEBUG) {
StartupLogger.i(String.format("Initializing %s", component.getName()));
}
//4、调用组件的onCreate()方法
result = initializer.create(mContext);
if (StartupLogger.DEBUG) {
StartupLogger.i(String.format("Initialized %s", component.getName()));
}
initializing.remove(component);
mInitialized.put(component, result);
return (T) result;
} finally {
Trace.endSection();
}
}
}
以上代码,大致也可分为四个步骤:
- 第一步:实例化该组件
- 第二步:得到当前组件下的所有依赖组件
- 第三步:递归依赖的组件,继续执行doInitialize()方法
- 第四步:调用组件的onCreate()方法
其实在第三步我们就可以发现,不需要配置依赖组件的关键所在。
4、结果
没运行前,我们理想中的结果,应该是先打印Lib2的日志,再打印Lib1的日志,看看如何。
是不是很炫酷!!!
五、补充
以上基本上介绍完startup的用法。其实,官网上提供了两种初始化组件的方法
- 自动初始化:在AndroidManifest.xml中配置启动
- 手动初始化(懒加载):代码启动
第一种方式,上面的代码已经介绍过了,好处就是,不需要代码管理,自动执行。
第二种方式,我们也可以尝试一下:
AppInitializer.getInstance(getApplicationContext()).initializeComponent(InitializerLib1.class);
一行代码搞定,我们看一下原理:
public <T> T initializeComponent(@NonNull Class<? extends Initializer<T>> component) {
return doInitialize(component, new HashSet<Class<?>>());
}
其实,最终还是无法逃离 doInitialize() 方法。这里就不做多余的复述了。
这种启动方式,操作上其实也简单,另外还有一个好处就是,可以加快app的启动速度,因为我们不需要在启动的时候就做耗时的初始化操作。 推荐使用!!!