Google推出的App Startup,主要是用于应用程序启动时,统一去管理初始化的组件库。该库可以去设置组件的初始化顺序,并且可以把所有组件都指定到一个ContentProriver里,避免存多个组件,多个contentProvider,从而提升app的启动时间,还可以通过懒加载的方式,去指定组件的初始化时机。下面内容会逐一介绍,并对App Startup进行源码解析。
一:为何使用App Startup,解决了什么问题?
一些库一般会提供给外部初始化方法,如下代码:
class MApp :Application(){
override fun onCreate() {
super.onCreate()
CrashReport.initCrashReport(getApplicationContext(), "注册时申请的APPID", false)
LeakCanary.install(this)
XXX.init(this)
XXX.install(this)
}
}
当使用的库一多就会出现大量类似上面的代码,不方便管理
第二种,由于ContentProvider的onCreate()方法是在Application的onCreate()之前,所以有一些开源的库的内部自己通过ContentProvider做了初始化,从而无需外面调用初始化代码。
public class XXXProvider extends ContentProvider {
@Override
public boolean onCreate() {
XXX.init(getContext());
return true;
}
...
}
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.jetpackappstartuptes">
<application>
<provider
android:authorities="${applicationId}.init-provider"
android:name=".XXXProvider"
android:exported="false"/>
</application>
</manifest>
如果每个第三方库内部都创建自己的ContentProvider去做初始化,而当我们使用到的这种开源库一多,那么就会存在创建多个ContentProvider的问题,从而影响App的启动时间。
解决这些问题,Google推出了App Startup
二:使用
1:添加依赖
dependencies {
// 使用App Startup
implementation "androidx.startup:startup-runtime:1.0.0"
}
2:实现Initializer接口
// Initializer需要实现两个接口
public interface Initializer<T> {
@NonNull
T create(@NonNull Context context);
@NonNull
List<Class<? extends Initializer<?>>> dependencies();
}
class BuglyInitializer :Initializer<Unit>{
override fun create(context: Context) {
CrashReport.initCrashReport(context, "注册时申请的APPID", false)
}
override fun dependencies(): List<Class<out Initializer<*>>> {
return emptyList()
}
}
泛型T为待初始化的Sdk对外提供的对象类型;
create(Context)方法是该Sdk初始化的地方,其参数context为Application Context,同时需要返回一个Sdk对外提供的对象实例。
dependencies()方法则需要返回一个列表,如果这个sdk是独立的没有依赖与其它的sdk,可以将该方法返回一个空列表(如上)。如果这个sdk依赖于其它的sdk,必须在其它sdk初始化之后才能初始化,则需要在dependencies()方法中指明。
如下:表示TestSDK1需要在TestSDK2初始化完后才初始化
class Test1SDKInitializer :Initializer<Test1SDK>{
override fun create(context: Context):Test1SDK{
Test1SDK.init(context)
return Test1SDK.getInstance()
}
override fun dependencies(): List<Class<out Initializer<*>>> {
val list = ArrayList<Class<out Initializer<*>>>()
list.add(Test2SDKInitializer::class.java)
return list
}
}
class Test2SDKInitializer :Initializer<Test2SDK>{
override fun create(context: Context):Test2SDK{
Test2SDK.init(context)
return Test2SDK.getInstance()
}
override fun dependencies(): List<Class<out Initializer<*>>> {
return emptyList()
}
}
3:在AndroidMainfest.xml里注册provider
<provider
android:authorities="${applicationId}.androidx-startup"
android:name="androidx.startup.InitializationProvider"
android:exported="false"
tools:node="merge">
<meta-data
android:name="com.example.jetpackappstartuptesty.BuglyInitializer"
android:value="@string/androidx_startup"/>
</provider>
通常每一个Initializer对应一个标签,但是如果有些Initializer已经被一个已 经注册的Initializer依赖(比如TestSDK2Initializer已经被TestSDK1Initializer依赖),那么 可以不用在AndroidManifest.xml文件中显式地指明,因为App Startup已经通过 注册的TestSDK1Initializer找到它了。
这里的标签的value属性必须指定为字符串androidx_startup的值, 也就是("androidx.startup"),否则将不生效。
如果有一个sdk内部通过App Startup帮助使用者处理了初始化,那么sdk的AndroidManifest.xml文件中已经存在了InitializationProvider的provider标签,此时会与app模块中的冲突,因此在app模块的provider标签中指明tools:node="merge",通过AndroidManifest.xml文件的合并机制。
4:App Startup懒加载实现
有些SDK我们不需要一开始就初始化,而只是在使用之前初始化就行,那么就可以考虑使用懒加载的形式,在使用SDK的功能之前调用如下代码进行初始化
// 使用之前调用
AppInitializer.getInstance(applicationContext).initializeComponent(BuglyInitializer::class.java)
并且在AndroidMainfest.xml里声明remove
<provider
android:authorities="${applicationId}.androidx-startup"
android:name="androidx.startup.InitializationProvider"
android:exported="false"
tools:node="merge">
<meta-data
android:name="com.example.jetpackappstartuptesty.BuglyInitializer"
android:value="@string/androidx_startup"
tools:node="remove"/>
</provider>
三:源码解析
App Startup比较简单,只有5个类,AppInitializer,InitializationProvider,Initializer,StartupException,StartupLogger。下面一个个看
// StartupLogger只是个log使用
public final class StartupLogger {
private StartupLogger() {
}
private static final String TAG = "StartupLogger";
static final boolean DEBUG = false;
public static void i(@NonNull String message) {
Log.i(TAG, message);
}
public static void e(@NonNull String message, @Nullable Throwable throwable) {
Log.e(TAG, message, throwable);
}
}
// StartupException只是定义了个异常
public final class StartupException extends RuntimeException {
public StartupException(@NonNull String message) {
super(message);
}
public StartupException(@NonNull Throwable throwable) {
super(throwable);
}
public StartupException(@NonNull String message, @NonNull Throwable throwable) {
super(message, throwable);
}
}
// Initializer只是个定义了个接口
public interface Initializer<T> {
@NonNull
T create(@NonNull Context context);
@NonNull
List<Class<? extends Initializer<?>>> dependencies();
}
// 自定义了个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;
}
@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.");
}
}
可以看到Initializer只是定义了个接口,StartupException自定义的异常类,StartupLogger打log的。InitializationProvider是自定义的ContentProvider。他的onCreate方法里调用了 AppInitializer.getInstance(context).discoverAndInitialize(); 所以关键是看AppInitializer的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();
}
}
- ComponentName provider = new ComponentName(mContext.getPackageName(), InitializationProvider.class.getName());获取InitializationProvider
- ProviderInfo providerInfo = mContext.getPackageManager()
.getProviderInfo(provider, GET_META_DATA);
Bundle metadata = providerInfo.metaData;拿到metadata信息 - for (String key : keys) 遍历metadata, if (startup.equals(value)) 判断value是不是androidx_startup(这就是为何value一定要是androidx_startup的原因)符合条件就把该组件添加到mDiscovered集合中。并执行组件的初始化doInitialize()
- final Set<Class>> mDiscovered;
mDiscovered = new HashSet<>();mDiscovered是一个HashSet
接着分析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();
}
}
}
我们可以看到,该方法主要执行了初始化,并且对依赖的组件也进行初始化
-
Object instance = component.getDeclaredConstructor().newInstance();
Initializer initializer = (Initializer) instance; 自己的初始化 -
List<Class>> dependencies = initializer.dependencies();获取到Sdk的组件集合
if (!dependencies.isEmpty()) { for (Class<? extends Initializer<?>> clazz : dependencies) { if (!mInitialized.containsKey(clazz)) { doInitialize(clazz, initializing); } } }
遍历做依赖的SDK的初始化。