Android Startup提供一种在应用启动时能够更加简单、高效的方式来初始化组件。开发人员可以使用Android Startup来简化启动序列,并显式地设置初始化顺序与组件之间的依赖关系。
前言
Android Startup提供一种在应用启动时能够更加简单、高效的方式来初始化组件。开发人员可以使用Android Startup来简化启动序列,并显式地设置初始化顺序与组件之间的依赖关系;
今天我们就来聊聊
一、使用步骤简单介绍
使用 AndroidX App Startup 来运行所有依赖项的初始化有两种方式:
自动初始化;
手动初始化(也是延迟初始化);
1、自动初始化
在 build.gradle 文件内添加依赖;
implementation "androidx.startup:startup-runtime:1.0.0-alpha01"
实现 Initializer 接口,并重写两个方法,来初始化组件;
public class MvpInitializer implements Initializer<Void> { @NonNull @Override public Void create(@NonNull Context context) { MvpManager.init(context); return null; } @NonNull @Override public List<Class<? extends Initializer<?>>> dependencies() { return new ArrayList<>(); } } ...... }
create(Context): 这里进行组件初始化工作;
dependencies(): 返回需要初始化的列表,同时设置 App 启动时依赖库运行的顺序;
在 AndroidManifest.xml 文件中注册 InitializationProvider;
<application> <provider android:authorities="${applicationId}.androidx-startup" android:name="androidx.startup.InitializationProvider" android:exported="false" tools:node="merge" > <!-- 自动初始化 --> <meta-data android:name="com.test.Initializer" android:value="androidx.startup"/> </provider> </application>
App 启动的时 App Startup 会读取 AndroidManifest.xml 文件里面的 InitializationProvider 下面的 声明要初始化的组件,完成自动初始化工作;
2、手动初始化(也是延迟初始化)
在 build.gradle 文件内添加依赖;
创建一个类 LibaryD 实现 Initializer 接口,并重写两个方法,来初始化组件;
在 AndroidManifest.xml 文件中注册 InitializationProvider
<application> <provider android:name="androidx.startup.InitializationProvider" android:authorities="${applicationId}.androidx-startup" android:exported="false" tools:node="merge"> <!-- 手动初始化(也是延迟初始化) --> <meta-data android:name="com.test.Initializer" android:value="androidx.startup" tools:node="remove" /> </provider> </application>
只需要在 标签内添加 tools:node="remove" 清单合并工具会将它从清单文件中删除;
在需要的地方进行初始化,调用以下代码进行初始化;
Initializer.getInstance(context).initializeComponent(Initializer::class.java);
如果组件初始化之后,再次调用 AppInitializer.initializeComponent() 方法不会再次初始化;
手动初始化(也是延迟初始化)是非常有用的,组件不需要在 App 启动时运行,只需要在需要它地方运行,可以减少 App 的启动时间,提高启动速度;
二、源码分析
1、InitializationProvider
在AndroidManifest文件中配置的组件名必须为androidx.startup.InitializationProvider,现在我们来看这个类的源码;
InitializationProvider.java 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; } @Override public Cursor query(...) { throw new IllegalStateException("Not allowed."); } @Override public String getType(...) { throw new IllegalStateException("Not allowed."); } @Nullable @Override public Uri insert(...) { throw new IllegalStateException("Not allowed."); } @Override public int delete(...) { throw new IllegalStateException("Not allowed."); } @Override public int update(...) { throw new IllegalStateException("Not allowed."); } }
InitializationProvider其实也是利用了 ContentProvider 的启动机制,在ContentProvider#onCreate(...)中执行初始化;
ContentProvider 的其他方法是没有意义的,所以都抛出了IllegalStateException;
2、自动初始化分析
App Startup 在 ContentProvider 中调用了AppInitializer#discoverAndInitialize()执行自动初始化;
AppInitializer是 App StartUp 框架的核心类,整个 App Startup 框架的代码其实非常少,其中很大部分核心代码都在 AppInitializer 类中;
2.1.AppInitializer.java discoverAndInitialize
final Set<Class<? extends Initializer<?>>> mDiscovered; void discoverAndInitialize() { 获取 androidx.startup.InitializationProvider 组件信息 ComponentName provider = new ComponentName(mContext.getPackageName(), InitializationProvider.class.getName()); ProviderInfo providerInfo = mContext.getPackageManager().getProviderInfo(provider, GET_META_DATA); androidx.startup 字符串 String startup = mContext.getString(R.string.androidx_startup); 获取组件信息中的 meta-data 数据 Bundle metadata = providerInfo.metaData; 遍历 meta-data 数据 if (metadata != null) { Set<Class<?>> initializing = new HashSet<>(); Set<String> keys = metadata.keySet(); for (String key : keys) { String value = metadata.getString(key, null); 判断 meta-data 数据中,value 为 androidx.startup 的键值对 if (startup.equals(value)) { Class<?> clazz = Class.forName(key); 检查指定的类是 Initializer 接口的实现类 if (Initializer.class.isAssignableFrom(clazz)) { Class<? extends Initializer<?>> component = (Class<? extends Initializer<?>>) clazz; 将 Class 添加到 mDiscovered Set 中 mDiscovered.add(component); 初始化此组件 doInitialize(component, initializing); } } } } } mDiscovered 用于判断组件是否已经自动启动 public boolean isEagerlyInitialized(@NonNull Class<? extends Initializer<?>> component) { return mDiscovered.contains(component); }
获取androidx.startup.InitializationProvider组件信息(在各个 Module 中声明的组件信息,会在manifest merger tool的处理下合并);
androidx.startup字符串;
获取组件信息中的 meta-data 数据;
遍历 meta-data 数据;
判断 meta-data 数据中,value 为 androidx.startup 的键值对;
检查指定的类是 Initializer 接口的实现类;
将 Class 添加到 mDiscovered Set 中,这将用于后续 判断组件是否已经自动启动;
初始化此组件;
2.2.AppInitializer.java AppInitializer.java
private static final Object sLock = new Object(); 缓存每个组件的初始化结果; final Map<Class<?>, Object> mInitialized; 初始化此组件 <T> T doInitialize(Class<? extends Initializer<?>> component, Set<Class<?>> initializing) { 对 sLock 加锁 Object result; 判断 initializing 中存在当前组件,说明存在循环依赖 if (initializing.contains(component)) { String message = String.format("Cannot initialize %s. Cycle detected.", component.getName()); throw new IllegalStateException(message); } 检查当前组件是否已初始化 if (!mInitialized.containsKey(component)) { 当前组件未初始化 记录正在初始化 initializing.add(component); 通过反射实例化 Initializer 接口实现类 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); 注意:这里将 initializing 作为参数传入 } } } 初始化当前组件 result = initializer.create(mContext); 移除正在初始化记录 initializing.remove(component); 缓存初始化结果 mInitialized.put(component, result); } else { 当前组件已经初始化,直接返回 result = mInitialized.get(component); } return (T) result; }
对 sLock 加锁;
判断 initializing 中存在当前组件,说明存在循环依赖(这是因为递归初始化所依赖的组件时,会将 initializing 作为参数传入,如果 initializing 中存在当前组件,说明依赖关系形成回环,如果不抛出异常,将形成无限递归);
检查当前组件是否已初始化;
记录正在初始化;
通过反射实例化 Initializer 接口实现类;
遍历所依赖的组件,如果所依赖的组件未初始化,递归调用doInitialize(...)执行初始化;
初始化当前组件;
移除正在初始化记录;
缓存初始化结果;
3、手动初始化源码分析
手动初始化(懒加载)的源码分析:
AppInitializer.java public <T> T initializeComponent(@NonNull Class<? extends Initializer<T>> component) { 调用 doInitialize(...) 方法: return doInitialize(component, new HashSet<Class<?>>()); }
其实非常简单,就是调用上一节的doInitialize(...)执行初始化。需要注意的是,这个方法是允许在子线程调用的,换句话说,自动初始化与手动初始化是存在线程同步问题的,那么 App Startup 是如何解决的呢?
前面有一个sLock没有说吗?其实它就是用来保证线程同步的锁:
<T> T doInitialize(Class<? extends Initializer<?>> component, Set<Class<?>> initializing) { 对 sLock 加锁 synchronized (sLock) { ... } }
总结
App Startup 是 Jetpack 的新成员,是为了解决因 App 启动时运行多个 ContentProvider 会增加 App 的启动时间的问题;
使用了一个 InitializationProvider 管理多个依赖项,消除了每个库单独使用 ContentProvider 成本,减少初始化时间;
App Startup 允许你自定义组件初始化顺序;
App Startup 提供了一种延迟初始化组件的方法,减少 App 初始化时间;
发表评论 取消回复