Android架构进阶之深入理解AppStartup原理

Android Startup提供一种在应用启动时能够更加简单、高效的方式来初始化组件。开发人员可以使用Android Startup来简化启动序列,并显式地设置初始化顺序与组件之间的依赖关系。
首页 新闻资讯 行业资讯 Android架构进阶之深入理解AppStartup原理

[[422993]]

前言

Android Startup提供一种在应用启动时能够更加简单、高效的方式来初始化组件。开发人员可以使用Android  Startup来简化启动序列,并显式地设置初始化顺序与组件之间的依赖关系;

今天我们就来聊聊

一、使用步骤简单介绍

使用 AndroidX App Startup 来运行所有依赖项的初始化有两种方式:

自动初始化;

手动初始化(也是延迟初始化);

1、自动初始化

在 build.gradle 文件内添加依赖;

复制

implementation "androidx.startup:startup-runtime:1.0.0-alpha01"
  • 1.

实现 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<>();     } }     ...... }
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

  • 9.

  • 10.

  • 11.

  • 12.

  • 13.

  • 14.

  • 15.

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>
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

  • 9.

  • 10.



 

 

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>
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

  • 9.

  • 10.

  • 11.

  • 12.

  • 13.

 

 

  • 只需要在    标签内添加 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.");     } }
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

  • 9.

  • 10.

  • 11.

  • 12.

  • 13.

  • 14.

  • 15.

  • 16.

  • 17.

  • 18.

  • 19.

  • 20.

  • 21.

  • 22.

  • 23.

  • 24.

  • 25.

  • 26.

  • 27.

  • 28.

  • 29.

  • 30.

  • 31.

  • 32.

  • 33.

  • 34.

  • 35.

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); }
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

  • 9.

  • 10.

  • 11.

  • 12.

  • 13.

  • 14.

  • 15.

  • 16.

  • 17.

  • 18.

  • 19.

  • 20.

  • 21.

  • 22.

  • 23.

  • 24.

  • 25.

  • 26.

  • 27.

  • 28.

  • 29.

  • 30.

  • 31.

  • 32.

  • 33.

  • 34.

  • 获取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; }
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

  • 9.

  • 10.

  • 11.

  • 12.

  • 13.

  • 14.

  • 15.

  • 16.

  • 17.

  • 18.

  • 19.

  • 20.

  • 21.

  • 22.

  • 23.

  • 24.

  • 25.

  • 26.

  • 27.

  • 28.

  • 29.

  • 30.

  • 31.

  • 32.

  • 33.

  • 34.

  • 35.

  • 36.

  • 37.

  • 38.

  • 39.

  • 40.

  • 41.

  • 42.

  • 对 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<?>>()); }
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

其实非常简单,就是调用上一节的doInitialize(...)执行初始化。需要注意的是,这个方法是允许在子线程调用的,换句话说,自动初始化与手动初始化是存在线程同步问题的,那么  App Startup 是如何解决的呢?

前面有一个sLock没有说吗?其实它就是用来保证线程同步的锁:

复制

<T> T doInitialize(Class<? extends Initializer<?>> component, Set<Class<?>> initializing) {    对 sLock 加锁     synchronized (sLock) {         ...      } }
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

总结

App Startup 是 Jetpack 的新成员,是为了解决因 App 启动时运行多个 ContentProvider 会增加 App  的启动时间的问题;

使用了一个 InitializationProvider 管理多个依赖项,消除了每个库单独使用 ContentProvider  成本,减少初始化时间;

App Startup 允许你自定义组件初始化顺序;

 

App Startup 提供了一种延迟初始化组件的方法,减少 App 初始化时间;

 

24    2021-09-10 07:31:54    Android AppStartup 原理