网站/小程序/APP个性化定制开发,二开,改版等服务,加扣:8582-36016

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

contenteditable="true" data-cke-enter-mode="2" data-cke-saved-name="i0" data-cke-widget-data="%7B%22url%22%3Anull%2C%22text%22%3A%22%5Cn%22%2C%22desc%22%3A%22%22%2C%22icon%22%3A%22%22%2C%22isCard%22%3Afalse%2C%22hasResquest%22%3Atrue%2C%22iconDefault%22%3A%22https%3A%2F%2Fcsdnimg.cn%2Frelease%2Fblog_editor_html%2Frelease1.9.1%2Fckeditor%2Fplugins%2FCsdnLink%2Ficons%2Ficon-default.png%3Ft%3DL892%22%2C%22id%22%3A%22TljEDt-1631240982692%22%2C%22classes%22%3Anull%7D" data-cke-widget-editable="text" data-cke-widget-keep-attr="0" data-cke-widget-upcasted="1" data-link-icon="https://csdnimg.cn/release/blog_editor_html/release1.9.1/ckeditor/plugins/CsdnLink/icons/icon-default.png?t=L892" data-link-title=" " data-widget="csdnlink" title=" " name="i0">

前言

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

今天我们就来聊聊

https%3A%2F%2Fcsdnimg.cn%2Frelease%2Fblog_editor_html%2Frelease1.9.1%2Fckeditor%2Fplugins%2FCsdnLink%2Ficons%2Ficon-default.png%3Ft%3DL892%22%2C%22id%22%3A%22AnQjxA-1631240982689%22%2C%22classes%22%3Anull%7D" data-cke-widget-editable="text" data-cke-widget-keep-attr="0" data-cke-widget-upcasted="1" data-link-icon="https://csdnimg.cn/release/blog_editor_html/release1.9.1/ckeditor/plugins/CsdnLink/icons/icon-default.png?t=L892" data-link-title=" " data-widget="csdnlink" title=" " name="i1">

一、使用步骤简单介绍

使用 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>

       
      1. 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 初始化时间;


                  评论 0

                  暂无评论
                  0
                  0
                  0
                  立即
                  投稿
                  发表
                  评论
                  返回
                  顶部