讲App startup前,先看看下面的代码,在Application里的onCreate()里面好多的init()方法。
public class MyApplication extends Application {
private static MyApplication instance;
public static MyApplication getInstance() {
return instance;
}
@Override
public void onCreate() {
super.onCreate();
instance = this;
initLog4J();
UMConfigure.init(this, UMConfigure.DEVICE_TYPE_PHONE, "xxxxxxxxxxxxxxxx");
ToastUtil.getInstance().init(this);
NetworkUtil.getInstance().init(this);
xxxxManager.getInstance().init(this);
P2PManager.getInstance().init(this);
DLNAManager.getInstance().init(this);
PaymentManager.getInstance().init(this);
ExternalADManager.getInstance().init(this);
GAManager.getInstance().init(this);
mUmengChannel = AnalyticsConfig.getChannel(this);
ErrorReporter.getInstance().init(this);
SharedInfoService.getInstance(this);
AbTestRemoteConfig.getInstance().setup();
}
}
之前可以用ContentProvider,把各个模块的初始化都分散出去。但是又导致了问题,ContentProvider毕竟是四大组件之一,听过一个空ContentProvider的初始化就耗费了2ms。那十个ContentProvider就20ms了。
而App startup的出现,既可以解决Application 的onCreate方法臃肿问题,又可以解决ContentProvider耗费性能的问题。
原理也很简单,就是在一个ContentProvider里面,把所有初始化都给办了。
但是你说我上面那种把初始都写在一起,和之后改用 app startup模式后,相比性能改善了,那也是不对的!
ContentProvider本身是拿来数据共享的。但是由于在app启动过程中,ContentProvider也会被系统自动调用onCreate()方法。所以大家都拿它来做启动初始化了。下面是ContentProvider的启动过程。都还在application启动之前。
简单使用:
这是广告模块的初始化。本来是要放到application onCreate()方法里面初始化的。
public class AdvInitializer implements Initializer<Object> {
@NonNull
@Override
public Object create(@NonNull Context context) {
AdvManager.getInstance().init(context);
return null;
}
@NonNull
@Override
public List<Class<? extends Initializer<?>>> dependencies() {
return Collections.emptyList();
}
}
在AndroidMenifest.xml中添加:
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<meta-data android:name="com.my.adv.AdvInitializer"
android:value="androidx.startup" />
</provider>
以上这两部做完就可以了。app启动的时候就会自动调用AdvInitializer 的create方法。
注意点:
1. dependencies() 方法的作用:解决多个模块初始化先后顺序的问题。
如果你的初始化还依赖别的模块先初始化,那么dependencies()就可以解决这个问题。返回你依赖模块的Initializer list就可以了。系统会帮你按顺序调用Initializer 。最后再调用你模块的Initializer。
比如:
@Override
public List<Class<Initializer<?>>> dependencies() {
// 定义模块依赖LogManager,需要先初始化LogManager。LogInitializer会先被调用,从而初始化
// LogManager,然后在初始化你的模块。
return Arrays.asList(LogInitializer.class);
}
2. 如果不想这样自动初始化所有了。那么只要把<provider>的tools:node="merge" 改成tools:node="remove"就好了。
3. 同样,不想让某个模块自动初始化, 那么也只要,在相应的<meta-data里>添加tools:node="remove"就好了。
4.当你把某个模块的初始化改成手动后。你可以直接调用下面的代码:
AppInitializer.getInstance(context)
.initializeComponent(AdvInitializer.class);
5. App startup的初始化都是在UI线程同步的。所以想要异步线程同步的同时,又支持先后顺序,那是不行的。
6. 有利于解决模块化依赖问题。当然本来就可以用ContentProvider解决。只是用App startup解决了多ContentProvider的耗时问题。
源码解析:
InitializationProvider .java
这个类就是AndroidManifest里定义的provider实现类。有用的代码就一句: AppInitializer.getInstance(context).discoverAndInitialize();
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.");
}
}
AppInitializer.java
再看这个类,重要的就这两个方法。
@SuppressWarnings("WeakerAccess")
public final class AppInitializer {
@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();
}
}
}
@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;
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();
}
}
}
- discoverAndInitialize方法中,解析获取Androidmanifest中定义的所有meta-data, 把所有的initializer 都放到mDiscovered里面存起来。调用doInitialize()完成初始化。
- doInitialize方法中,完成各个模块的初始化。并且用递归调用doInitialize解决链条依赖问题。里面还是用了反射来初始化各个模块的initializer
再总结一下:
1.底层是contentprovider
2.contentprovider初始化生命周期在 attachebaseContext,然后是contentprovider oncreate ,然后才是Application onCreate
3.用contentprovider可以省去三方库的 context问题
4.但是三方库都用contentprovider 这个办法,本身也耗时。
5.所以用一个contentprovider 串行三方库的初始化问题。
6.如果不想在contentprovider的生命周期就初始化想延迟初始化怎么办?remove