-
Startup
添加依赖:
dependencies { implementation("androidx.startup:startup-runtime:1.1.1") }
manifest中加入:
<provider android:name="androidx.startup.InitializationProvider" android:authorities="${applicationId}.androidx-startup" android:exported="false" tools:node="merge"> <meta-data android:name="com.mph.review.startup.CCInitializer" android:value="androidx.startup" /> </provider>
merge属性可以确保和其他manifest合并后消除冲突;exported表示该content provider是私有的,provider中的其他属性都是固定的,authorities属性是根据applicationId确定的。
meta-data标签中就是自定义Initializer放置的位置,可以定义多个meta-data标签声明多个Initializer,name是完整类名,value是固定的。
class CCInitializer : Initializer<User> { override fun create(context : Context) : User { val uu = User("CC") uu.name = "CC" print("CC") return uu } override fun dependencies() : MutableList<Class<out Initializer<*>>> { return mutableListOf(PPInitializer::class.java) } }
其中dependencies中定义的是该Initializer所依赖的其他Initializer,在初始化当前Initializer之前会先初始化它所有依赖的Initializer,比如这里的PPInitializer会比CCInitializer优先初始化,至于dependencies中返回的Initializer集合则会按照集合的顺序从头开始初始化,下面解析代码的时候会看到。
-
解析
在InitializationProvider创建的时候会调用它的onCreate方法:
@Override public final boolean onCreate() { Context context = getContext(); if (context != null) { AppInitializer.getInstance(context).discoverAndInitialize(); } else { throw new StartupException("Context cannot be null"); } return true; }
这里会调用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(); } }
首先会调用PackageManager的getProviderInfo方法获取meta-data信息,mContext.getString(R.string.androidx_startup)得到的字面值就是“androidx.startup”,遍历所有的meta-data,找出所有value是“androidx.startup”的,取它们的key,也就是manifest中meta-data中name属性的值作为类名,把它的Class对象存入mDiscovered集合,然后调用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(); } } }
结合代码我们看到,这里会先把当前Initializer的Class存入initializing中,然后通过反射构造当前Initializer,在调用它的create方法之前,会先调用它的dependencies方法,然后对它返回的集合进行递归doInitialize调用,这就是为什么依赖的Initializer会先初始化的原因。因为dependencies中返回的是List,所以这里的循环会按照List的顺序从头开始,所以dependencies集合中越靠前的会更先调用create方法初始化。
注意,在discoverAndInitialize方法中调用doInitialize方法时传入的是一个空的Set集合,然后在doInitialize方法中递归传递的也都是这个Set,你可能会说Set不是无序的嘛,其实这里的Set只是为了确保不添加重复的数据,因为整个过程中只调用了它的add和remove方法,没有取过数据,所以Set的无序性是无影响的,这里的用处只是暂存待初始化的Initializer而已,你可以把它换成一个类变量,是一样的作用,因为doInitialize方法是在discoverAndInitialize中开启调用的,所以递归初始化完成后最终还是会回到discoverAndInitialize方法,所以这里使用一个方法内变量作为共享变量是更合理的,使用类变量就有些重量级了。
AppInitializer.getInstance(this).initializeComponent(CCInitializer::class.java)
最后,调用AppInitializer实例的initializeComponent方法就可以获取启动时已初始化好的相关实例了,在上面的流程中,初始化的实力会保存到mInitialized中,所以调用initializeComponent方法获取的时候会优先从mInitialized取,如果mInitialized中没有的话则会再次执行上述初始化流程,只不过此时执行的初始化只是针对单个的Initializer而已,可见,你也可以手动调用AppInitializer来完成初始化工作。
-
应用场景
为什么要使用Startup?
因为ContenProvider的
onCreate
调用在Application.attachBaseContext()
和Application.onCreate()
之间发生,所以很多在应用启动之后就要发挥作用的库都会选择通过在ContenProvider的onCreate方法中进行初始化,但是ContenProvider作为一个重量级组件,它本身的启动和初始化是需要一定时间的,所以如果ContenProvider数量过多会造成一定的性能损耗,所以Startup的目的就是把所有的用于初始化的ContenProvider都合并成一个,这样就减少了大量的ContenProvider创建性能损耗和内存占用。但是,Startup无法作用于你所依赖的第三方库,因为你没有权限去修改第三方库的manifest文件,所以第三方库中的ContenProvider还是会存在,Startup只能使你自定义的初始化工作合并到一个ContenProvider中去,所以它的场景是:
在你需要创建一个需要在启动时初始化的类库,或者你的app在启动时就需要很多本地初始化工作的时候,你可以选择使用Startup框架来处理,而且你可以通过Startup让初始化业务分门别类、顺序化。
总之,Startup会在项目庞大或者有类库需要的时候才会有用武之地。
网友评论