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

前言

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 { 
  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>> dependencies() { 
  11.         return new ArrayList<>(); 
  12.     } 
  13.     ...... 

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

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

在 AndroidManifest.xml 文件中注册 InitializationProvider;

 
 
 
 
  1.  
  2.         
  3.             android:authorities="${applicationId}.androidx-startup" 
  4.             android:name="androidx.startup.InitializationProvider" 
  5.             android:exported="false" 
  6.             tools:node="merge" > 
  7.            
  8.              
  9.      
  10.  

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

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

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

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

在 AndroidManifest.xml 文件中注册 InitializationProvider

 
 
 
 
  1.  
  2.         
  3.             android:name="androidx.startup.InitializationProvider" 
  4.             android:authorities="${applicationId}.androidx-startup" 
  5.             android:exported="false" 
  6.             tools:node="merge"> 
  7.              
  8.             
  9.                 android:name="com.test.Initializer" 
  10.                 android:value="androidx.startup" 
  11.                 tools:node="remove" /> 
  12.          
  13.      

  • 只需要在 标签内添加 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>> 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> initializing = new HashSet<>(); 
  13.         Set keys = metadata.keySet(); 
  14.         for (String key : keys) { 
  15.             String value = metadata.getString(key, null); 
  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> component = (Class>) 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> 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, Object> mInitialized; 
  4. 初始化此组件 
  5.  T doInitialize(Class> component, Set> 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>> dependencies = initializer.dependencies(); 
  23.         if (!dependencies.isEmpty()) { 
  24.             for (Class> 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 initializeComponent(@NonNull Class> component) { 
  3.     调用 doInitialize(...) 方法: 
  4.     return doInitialize(component, new HashSet>()); 

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

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

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

总结

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

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

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

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

分享题目:Android架构进阶之深入理解AppStartup原理
文章起源:http://www.gawzjz.com/qtweb/news49/199099.html

网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联