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

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

[[422993]]

前言

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

今天我们就来聊聊

一、使用步骤简单介绍

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

自动初始化;

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

1、自动初始化

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

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

实现 Initializer 接口,并重写两个方法,来初始化组件;

  1. public class MvpInitializer implements Initializer<Void> { 
  2.     @NonNull 
  3.     @Override 
  4.     public Void create(@NonNull Context context) { 
  5.          MvpManager.init(context); 
  6.          return null
  7.     } 
  8.     @NonNull 
  9.     @Override 
  10.     public List<Class<? extends Initializer<?>>> dependencies() { 
  11.         return new ArrayList<>(); 
  12.     } 
  13.     ...... 

create(Context): 这里进行组件初始化工作;

dependencies(): 返回需要初始化的列表,同时设置 App 启动时依赖库运行的顺序;

在 AndroidManifest.xml 文件中注册 InitializationProvider;

  1. <application> 
  2.         <provider 
  3.             android:authorities="${applicationId}.androidx-startup" 
  4.             android:name="androidx.startup.InitializationProvider" 
  5.             android:exported="false" 
  6.             tools:node="merge" > 
  7.           <!-- 自动初始化 --> 
  8.             <meta-data android:name="com.test.Initializer" android:value="androidx.startup"/> 
  9.     </provider> 
  10. </application> 

 

 

App 启动的时 App Startup 会读取 AndroidManifest.xml 文件里面的 InitializationProvider 下面的 声明要初始化的组件,完成自动初始化工作;

2、手动初始化(也是延迟初始化)

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

创建一个类 LibaryD 实现 Initializer 接口,并重写两个方法,来初始化组件;

在 AndroidManifest.xml 文件中注册 InitializationProvider

  1. <application> 
  2.         <provider 
  3.             android:name="androidx.startup.InitializationProvider" 
  4.             android:authorities="${applicationId}.androidx-startup" 
  5.             android:exported="false" 
  6.             tools:node="merge"
  7.             <!-- 手动初始化(也是延迟初始化) --> 
  8.             <meta-data 
  9.                 android:name="com.test.Initializer" 
  10.                 android:value="androidx.startup" 
  11.                 tools:node="remove" /> 
  12.         </provider> 
  13.     </application> 

 

 

  • 只需要在 标签内添加 tools:node="remove" 清单合并工具会将它从清单文件中删除;
  • 在需要的地方进行初始化,调用以下代码进行初始化;
  • Initializer.getInstance(context).initializeComponent(Initializer::class.java);
  • 如果组件初始化之后,再次调用 AppInitializer.initializeComponent() 方法不会再次初始化;
  • 手动初始化(也是延迟初始化)是非常有用的,组件不需要在 App 启动时运行,只需要在需要它地方运行,可以减少 App 的启动时间,提高启动速度;

二、源码分析

1、InitializationProvider

在AndroidManifest文件中配置的组件名必须为androidx.startup.InitializationProvider,现在我们来看这个类的源码;

  1. InitializationProvider.java 
  2. public final class InitializationProvider extends ContentProvider { 
  3.     @Override 
  4.     public boolean onCreate() { 
  5.         Context context = getContext(); 
  6.         if (context != null) { 
  7.             初始化 
  8.             AppInitializer.getInstance(context).discoverAndInitialize(); 
  9.         } else { 
  10.             throw new StartupException("Context cannot be null"); 
  11.         } 
  12.         return true
  13.     } 
  14.     @Override 
  15.     public Cursor query(...) { 
  16.         throw new IllegalStateException("Not allowed."); 
  17.     } 
  18.     @Override 
  19.     public String getType(...) { 
  20.         throw new IllegalStateException("Not allowed."); 
  21.     } 
  22.     @Nullable 
  23.     @Override 
  24.     public Uri insert(...) { 
  25.         throw new IllegalStateException("Not allowed."); 
  26.     } 
  27.     @Override 
  28.     public int delete(...) { 
  29.         throw new IllegalStateException("Not allowed."); 
  30.     } 
  31.     @Override 
  32.     public int update(...) { 
  33.         throw new IllegalStateException("Not allowed."); 
  34.     } 

InitializationProvider其实也是利用了 ContentProvider 的启动机制,在ContentProvider#onCreate(...)中执行初始化;

ContentProvider 的其他方法是没有意义的,所以都抛出了IllegalStateException;

2、自动初始化分析

App Startup 在 ContentProvider 中调用了AppInitializer#discoverAndInitialize()执行自动初始化;

AppInitializer是 App StartUp 框架的核心类,整个 App Startup 框架的代码其实非常少,其中很大部分核心代码都在 AppInitializer 类中;

2.1.AppInitializer.java discoverAndInitialize

  1. final Set<Class<? extends Initializer<?>>> mDiscovered; 
  2. void discoverAndInitialize() { 
  3.     获取 androidx.startup.InitializationProvider 组件信息 
  4.     ComponentName provider = new ComponentName(mContext.getPackageName(), InitializationProvider.class.getName()); 
  5.     ProviderInfo providerInfo = mContext.getPackageManager().getProviderInfo(provider, GET_META_DATA); 
  6.    androidx.startup 字符串 
  7.     String startup = mContext.getString(R.string.androidx_startup); 
  8.    获取组件信息中的 meta-data 数据 
  9.     Bundle metadata = providerInfo.metaData; 
  10.     遍历 meta-data 数据 
  11.     if (metadata != null) { 
  12.         Set<Class<?>> initializing = new HashSet<>(); 
  13.         Set<String> keys = metadata.keySet(); 
  14.         for (String key : keys) { 
  15.             String value = metadata.getString(keynull); 
  16.           判断 meta-data 数据中,value 为 androidx.startup 的键值对 
  17.             if (startup.equals(value)) { 
  18.                 Class<?> clazz = Class.forName(key); 
  19.                  检查指定的类是 Initializer 接口的实现类 
  20.                 if (Initializer.class.isAssignableFrom(clazz)) { 
  21.                     Class<? extends Initializer<?>> component = (Class<? extends Initializer<?>>) clazz; 
  22.                     将 Class 添加到 mDiscovered Set 中 
  23.                     mDiscovered.add(component); 
  24.                     初始化此组件 
  25.                     doInitialize(component, initializing); 
  26.                 } 
  27.             } 
  28.         } 
  29.     } 
  30. mDiscovered 用于判断组件是否已经自动启动 
  31. public boolean isEagerlyInitialized(@NonNull Class<? extends Initializer<?>> component) { 
  32.     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

  1. private static final Object sLock = new Object(); 
  2. 缓存每个组件的初始化结果; 
  3. final Map<Class<?>, Object> mInitialized; 
  4. 初始化此组件 
  5. <T> T doInitialize(Class<? extends Initializer<?>> component, Set<Class<?>> initializing) { 
  6.     对 sLock 加锁 
  7.     Object result; 
  8.   判断 initializing 中存在当前组件,说明存在循环依赖 
  9.     if (initializing.contains(component)) { 
  10.         String message = String.format("Cannot initialize %s. Cycle detected.", component.getName()); 
  11.         throw new IllegalStateException(message); 
  12.     } 
  13.    检查当前组件是否已初始化 
  14.     if (!mInitialized.containsKey(component)) { 
  15.         当前组件未初始化 
  16.         记录正在初始化 
  17.         initializing.add(component); 
  18.         通过反射实例化 Initializer 接口实现类 
  19.         Object instance = component.getDeclaredConstructor().newInstance(); 
  20.         Initializer<?> initializer = (Initializer<?>) instance; 
  21.        遍历所依赖的组件 
  22.         List<Class<? extends Initializer<?>>> dependencies = initializer.dependencies(); 
  23.         if (!dependencies.isEmpty()) { 
  24.             for (Class<? extends Initializer<?>> clazz : dependencies) { 
  25.                 如果所依赖的组件未初始化,递归执行初始化 
  26.                 if (!mInitialized.containsKey(clazz)) { 
  27.                     doInitialize(clazz, initializing); 注意:这里将 initializing 作为参数传入 
  28.                 } 
  29.             } 
  30.         } 
  31.        初始化当前组件 
  32.         result = initializer.create(mContext); 
  33.        移除正在初始化记录 
  34.         initializing.remove(component); 
  35.         缓存初始化结果 
  36.         mInitialized.put(component, result); 
  37.     } else { 
  38.         当前组件已经初始化,直接返回 
  39.         result = mInitialized.get(component); 
  40.     } 
  41.      return (T) result; 
  • 对 sLock 加锁;
  • 判断 initializing 中存在当前组件,说明存在循环依赖(这是因为递归初始化所依赖的组件时,会将 initializing 作为参数传入,如果 initializing 中存在当前组件,说明依赖关系形成回环,如果不抛出异常,将形成无限递归);
  • 检查当前组件是否已初始化;
  • 记录正在初始化;
  • 通过反射实例化 Initializer 接口实现类;
  • 遍历所依赖的组件,如果所依赖的组件未初始化,递归调用doInitialize(...)执行初始化;
  • 初始化当前组件;
  • 移除正在初始化记录;
  • 缓存初始化结果;

3、手动初始化源码分析

手动初始化(懒加载)的源码分析:

  1. AppInitializer.java 
  2. public <T> T initializeComponent(@NonNull Class<? extends Initializer<T>> component) { 
  3.     调用 doInitialize(...) 方法: 
  4.     return doInitialize(component, new HashSet<Class<?>>()); 

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

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

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

总结

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

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

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

 

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

 

责任编辑:武晓燕 来源: Android开发编程
相关推荐

2021-09-08 06:51:52

AndroidRetrofit原理

2021-10-15 09:19:17

AndroidSharedPrefe分析源码

2021-09-17 06:55:50

AndroidLayoutView

2022-09-05 22:22:00

Stream操作对象

2022-01-14 12:28:18

架构OpenFeign远程

2021-08-24 07:53:28

AndroidActivity生命周期

2019-03-18 09:50:44

Nginx架构服务器

2018-12-27 12:34:42

HadoopHDFS分布式系统

2023-01-16 18:32:15

架构APNacos

2021-09-16 06:44:04

Android进阶流程

2014-07-15 17:17:31

AdapterAndroid

2021-09-15 07:31:33

Android窗口管理

2021-10-26 17:52:52

Android插件化技术

2021-09-24 08:10:40

Java 语言 Java 基础

2017-08-08 09:15:41

前端JavaScript页面渲染

2021-10-21 10:02:37

Java开发代码

2021-10-10 13:31:14

Java负载均衡算法

2021-09-30 07:36:51

AndroidViewDraw

2017-08-15 13:05:58

Serverless架构开发运维

2021-02-17 11:25:33

前端JavaScriptthis
点赞
收藏

51CTO技术栈公众号