- 首先Startup的官方文档地址如下:
https://developer.android.google.cn/topic/libraries/app-startup#kotlin
官方的定义如下:
The App Startup library provides a straightforward, performant way to initialize components at application startup. Both library developers and app developers can use App Startup to streamline startup sequences and explicitly set the order of initialization.
Instead of defining separate content providers for each component you need to initialize, App Startup allows you to define component initializers that share a single content provider. This can significantly improve app startup time.
翻译如下:
应用程序启动库提供了一种在应用程序启动时初始化组件的简单、高效的方法。库开发人员和应用程序开发人员都可以使用StartUp
来简化启动序列并显式设置初始化顺序。
StartUp
允许您定义共享单个内容提供程序的组件初始化程序,而不是为每个需要初始化的组件定义单独的content provider
。这可以显著缩短应用程序启动时间。
简单的说就是通过一个公共的content provider
来集中管理需要初始化的组件,从而提高应用的启动速度。
StartUp的使用方法
- 添加所需依赖
dependencies {
implementation "androidx.startup:startup-runtime:1.0.0"
}
- 为需要的每一个组件定义一个
component initializer
,假设存在A,B,C,D四个需要初始化的组件,这时候就需要定义四个initializer
.
class ASdk {
//假设这里是我们需要初始化的组件A
companion object {
fun getInstance(): ASdk {
return Instance.instance
}
}
private object Instance {
val instance = ASdk()
}
}
每一个需要初始化的组件我们需要创建一个class
去实现Initializer
接口,它所对应的Initializer
如下:
class ASdkInitializer : Initializer {
override fun create(context: Context): ASdk {
Log.i("gj","ASdkInitializer create()方法执行" )
return ASdk.getInstance()
}
override fun dependencies(): MutableList>> {
Log.i("gj","ASdkInitializer dependencies()方法执行" )
return mutableListOf()
}
}
可以看到只有两个方法需要我们去实现,create ()
方法和dependencies()
方法。
-
create ()
方法包含初始化组件所需的所有操作,并返回T的实例。 -
dependencies()
方法返回的是一个Initializer
的list
,这个集合当中包含了当前的Initializer
所依赖的其他的Initializer
,由此可见该方法的作用是让我们可以控制在程序启动时的组件的初始化顺序。
比如我们的组件A,B,C的初始化之间存在着C依赖B,B依赖A的这么一种关系,这时B和C组件的Initializer
就应该写成如下:
/**
* B组件的初始化Initializer,依赖A
*/
class BSdkInitializer : Initializer {
override fun create(context: Context): BSdk {
Log.i("gj","BSdkInitializer create()方法执行" )
return BSdk.getInstance()
}
override fun dependencies(): MutableList>> {
Log.i("gj","BSdkInitializer dependencies()方法执行" )
return mutableListOf(ASdkInitializer::class.java)
}
}
因为B组件依赖以A组件的初始化完成,所以在dependencies()
方法中,我们需要返回的是ASdkInitializer
,同理C的Initializer
如下:
/**
* C组件的初始化Initializer,依赖B
*/
class CSdkInitializer : Initializer {
override fun create(context: Context): CSdk {
Log.i("gj", "CSdkInitializer create()方法执行")
return CSdk.getInstance()
}
override fun dependencies(): MutableList>> {
Log.i("gj", "CSdkInitializer dependencies()方法执行")
return mutableListOf(BSdkInitializer::class.java)
}
}
至此,我们的对组件的定义基本完成,接下来StartUp
是怎么启动的?
StartUp
为我们提供了两种方式来启动,一种是自动启动,一种是手动调用启动。
- 自动启动的方式如下:
我们只需要在AndroidManifest中对InitializationProvider
添加对应声明,如下:
meta-data
标签下是我们Initializer
的路径,value
需要注意的是必须为androidx.startup
,可以看到在上面,A,B,C组件中我只对C的Initializer
做了声明,这是因为B和C都可以通过dependencies()
的链式调用进行初始化,当然,如果A,B,C之间不存在依赖关系的话,则需要对每一个对应的Initializer
进行声明。而A,B,C的执行顺序则与我们的声明顺序保持一致。
至此,程序启动的时候就会按照我们既定的顺序进行初始化操作。
运行后日志如下:
从日志不难看出:
creat()
的执行顺序为A-->B-->C,dependencies()
的执行顺序为C-->B-->A,符合我们的预期效果。
- 手动控制方式如下:
比如我们还定义了一个DSdkInitializer
,这时候需要对D组件手动进行初始化,我们就可以直接对其进行初始化调用:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//手动启动sdk的初始化
AppInitializer.getInstance(this).initializeComponent(DSdkInitializer::class.java)
}
}
从日志也可以看到D组件是最后被初始化的。
这里我们需要注意的是,我们虽然在
AndroidManifest
中声明了DSdkInitializer
但是这个声明并不是了为了D组件的初始化所做的,可以看到我们加了一个属性tools:node="remove"
这个标签的作用是为了防止在其他引用的三方库中有对相同组件的一个初始化,保证该组件的自动初始化真正的被关闭。
- 关闭startup的所有组件的自动初始化,我们除了可以上诉一个一个关闭的方法,还可以调用如下的方法:
这样就可以做到真正的关闭 Startup 的所有自动初始化逻辑。
StartUp 源码详解
由上图可以看到StartUp包含的类只有五个AppInitializer
,InitializationProvider
,Initializer
,StartupException
,StartupLogger
下面我们依次对这五个类进行详细的介绍。
AppInitializer
这个类是StartUp类库的核心类。
public final class AppInitializer {
...
private static AppInitializer sInstance;
/**
* Guards app initialization.
*/
private static final Object sLock = new Object();
@NonNull
final Map, Object> mInitialized;
@NonNull
final Context mContext;
/**
* Creates an instance of {@link AppInitializer}
*
* @param context The application context
*/
AppInitializer(@NonNull Context context) {
mContext = context.getApplicationContext();
mInitialized = new HashMap<>();
}
/**
* @param context The Application {@link Context}
* @return The instance of {@link AppInitializer} after initialization.
*/
@NonNull
@SuppressWarnings("UnusedReturnValue")
public static AppInitializer getInstance(@NonNull Context context) {
synchronized (sLock) {
if (sInstance == null) {
sInstance = new AppInitializer(context);
}
return sInstance;
}
}
...
}
首先通过该类的getInstance(@NonNull Context context)
方法获取我们所需的动态实例。
这里注意一下这个参数的作用`Map
@NonNull
@SuppressWarnings("unused")
public T initializeComponent(@NonNull Class extends Initializer> component) {
return doInitialize(component, new HashSet>());
}
@NonNull
@SuppressWarnings({"unchecked", "TypeParameterUnusedInFormals"})
T doInitialize(
@NonNull Class extends Initializer>> component,
@NonNull Set> 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>> 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();
}
}
}
从上面的代码可以看到doInitialize(component, new HashSet
方法最终调用的是result = initializer.create(mContext);
不难看出这个方法最终的目的就是完成所有依赖项的初始化。
大致的流程如下:
- 首先会判断正在进行初始化的
Initializer
集合,如果集合中存在component
,说明当前Initializer
之间存在着循环依赖,会抛出一个循环依赖的异常,如果没有依赖则继续向下执行。 - 如果已经初始化过的
mInitialized
中不包含component
,说明当前的Initializer
并为进行初始化,将其加到正在初始化的集合initializing
中,之后通过反射调用component
的构造方法进行初始化,同时获取Initializer
的所有的依赖项记作dependencies
,如果dependencies
当中存在依赖,则对每个依赖项通过递归调用doInitialize(clazz, initializing)
的方式进行初始化。 - 最终调用
initializer.create(mContext)
完成初始化,并将已经初始化的Initializer
从集合initializing
移除,同时将初始化完成的component
放到mInitialized
中保存起来。 - 如果已经在
mInitialized
中包含了component
,就只需要从result = mInitialized.get(component);
中获取缓存即可。
下面看一下discoverAndInitialize()
方法做了什么?
该方法是由InitializationProvider
进行调用,最终会调用的是我们上面提到过的方法doInitialize(component, initializing);
@SuppressWarnings("unchecked")
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> initializing = new HashSet<>();
Set 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;
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();
}
}
- 首先,会获取到
InitializationProvider
中所有的metadata
- 接着遍历所有的
metadata
,找到属于startup的所有metadata
,并通过包名路径查看是否是Initializer
的实现类,如果是的话就进行初始化的操作。
InitializationProvider
InitializationProvider
继承自ContentProvider
,主要作用就是触发对StartUp的整个初始化。
/**
* The {@link ContentProvider} which discovers {@link Initializer}s in an application and
* initializes them before {@link Application#onCreate()}.
*
* @hide
*/
@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;
}
.....
}
onCreate()
方法由系统主动触发。
Initializer
该类就是StartUp提供的需要我们去声明初始化的组件以及初始化的依赖关系和顺序的。
/**
* {@link Initializer}s can be used to initialize libraries during app startup, without
* the need to use additional {@link android.content.ContentProvider}s.
*
* @param The instance type being initialized
*/
public interface Initializer {
/**
* 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.
*
* For e.g. if a {@link Initializer} `B` defines another
* {@link Initializer} `A` as its dependency, then `A` gets initialized before `B`.
*/
@NonNull
List>> dependencies();
}
-
T create(@NonNull Context context);
方法中完成初始化并返回。 -
List
方法中指定当前>> dependencies(); Initializer
的依赖关系。
StartupException
@RestrictTo(RestrictTo.Scope.LIBRARY)
@SuppressWarnings("WeakerAccess")
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);
}
}
RuntimeException
的一个自定义的子类,用于StartUp初始化过程中遇到错误的抛出类。
StartupLogger
public final class StartupLogger {
private StartupLogger() {
// Does nothing.
}
/**
* The log tag.
*/
private static final String TAG = "StartupLogger";
/**
* To enable logging set this to true.
*/
static final boolean DEBUG = false;
/**
* Info level logging.
*
* @param message The message being logged
*/
public static void i(@NonNull String message) {
Log.i(TAG, message);
}
/**
* Error level logging
*
* @param message The message being logged
* @param throwable The optional {@link Throwable} exception
*/
public static void e(@NonNull String message, @Nullable Throwable throwable) {
Log.e(TAG, message, throwable);
}
}
这个类就是一个普通的工具类,没什么好说的。