- 首先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<T>
接口,它所對應的Initializer
如下:
class ASdkInitializer : Initializer<ASdk> {
override fun create(context: Context): ASdk {
Log.i("gj","ASdkInitializer create()方法執行" )
return ASdk.getInstance()
}
override fun dependencies(): MutableList<Class<out Initializer<*>>> {
Log.i("gj","ASdkInitializer dependencies()方法執行" )
return mutableListOf()
}
}
可以看到只有兩個方法需要我們去實現,create ()
方法和dependencies()
方法。
-
create ()
方法包含初始化組件所需的所有操作,并返回T的實例。 -
dependencies()
方法返回的是一個Initializer<T>
的list
,這個集合當中包含了當前的Initializer<T>
所依賴的其他的Initializer<T>
,由此可見該方法的作用是讓我們可以控制在程序啟動時的組件的初始化順序。
比如我們的組件A,B,C的初始化之間存在著C依賴B,B依賴A的這么一種關系,這時B和C組件的Initializer<T>
就應該寫成如下:
/**
* B組件的初始化Initializer,依賴A
*/
class BSdkInitializer : Initializer<BSdk> {
override fun create(context: Context): BSdk {
Log.i("gj","BSdkInitializer create()方法執行" )
return BSdk.getInstance()
}
override fun dependencies(): MutableList<Class<out Initializer<*>>> {
Log.i("gj","BSdkInitializer dependencies()方法執行" )
return mutableListOf(ASdkInitializer::class.java)
}
}
因為B組件依賴以A組件的初始化完成,所以在dependencies()
方法中,我們需要返回的是ASdkInitializer
,同理C的Initializer
如下:
/**
* C組件的初始化Initializer,依賴B
*/
class CSdkInitializer : Initializer<CSdk> {
override fun create(context: Context): CSdk {
Log.i("gj", "CSdkInitializer create()方法執行")
return CSdk.getInstance()
}
override fun dependencies(): MutableList<Class<out Initializer<*>>> {
Log.i("gj", "CSdkInitializer dependencies()方法執行")
return mutableListOf(BSdkInitializer::class.java)
}
}
至此,我們的對組件的定義基本完成,接下來StartUp
是怎么啟動的?
StartUp
為我們提供了兩種方式來啟動,一種是自動啟動,一種是手動調用啟動。
- 自動啟動的方式如下:
我們只需要在AndroidManifest中對InitializationProvider
添加對應聲明,如下:
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<meta-data
android:name="com.yy.myapplication.CSdkInitializer"
android:value="androidx.startup"/>
<meta-data
android:name="com.yy.myapplication.DSdkInitializer"
android:value="androidx.startup"
tools:node="remove" />
</provider>
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的所有組件的自動初始化,我們除了可以上訴一個一個關閉的方法,還可以調用如下的方法:
<provider android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
tools:node="remove"/>
這樣就可以做到真正的關閉 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<Class<?>, 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<Class<?>, Object> 用map去存儲已經被初始化過的組件。
@NonNull
@SuppressWarnings("unused")
public <T> T initializeComponent(@NonNull Class<? extends Initializer<T>> component) {
return doInitialize(component, new HashSet<Class<?>>());
}
@NonNull
@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()));
}
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<Class<?>>())
方法最終調用的是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<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;
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 <T> The instance type being initialized
*/
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();
}
-
T create(@NonNull Context context);
方法中完成初始化并返回。 -
List<Class<? extends Initializer<?>>> 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);
}
}
這個類就是一個普通的工具類,沒什么好說的。