jetpack之App Startup

    技术2022-07-11  115

    一、前言

    最近jetpack又有新成员了–App Startup。刚开始看到这个字面理解就是和app启动相关的。我们来看一下官方给出的解释: 看不懂?没关系,大致意思就是:提供了在 App 启动时初始化组件简单、高效的方法,无论是 library 开发人员还是 App 开发人员都可以使用 App Startup 显示的设置初始化顺序,简单的说就是 App Startup 提供了一个 ContentProvider 来运行所有依赖项的初始化,避免每个第三方库单独使用 ContentProvider 进行初始化,从而提高了应用的程序的启动速度。

    二、WorkManagerInitializer

    App 启动运行时会初始化一些逻辑,它们为了方便开发者使用,避免开发者手动调用,使用 ContentProvider 进行初始化,官方还提供了WorkManager的启动方式的示例,说明了它是如何通过WorkManagerInitializer 启动的。在AndroidManifest.xml文件中添加如下配置: 我们载来看看WorkManagerInitializer的源码

    public class WorkManagerInitializer extends ContentProvider { @Override public boolean onCreate() { // Initialize WorkManager with the default configuration. WorkManager.initialize(getContext(), new Configuration.Builder().build()); return true; } .... }

    关键方法就是onCreate()方法。

    可以看到当前类继承了ContentProvider ,都知道它是android跨进程通信的一种方式在onCreate()中,进行了WorkManager的初始化操作。

    知道了以上两点,我们自己也可以模仿WorkManagerInitializer ,看看具体操作。

    三、初体验

    1、自定义 Lib1

    代码如下:

    public class Lib1 extends ContentProvider { @Override public boolean onCreate() { Log.e("Lib1初始化》》》》》》》","1111111111111"); return true; } }
    2、自定义 Lib2

    代码如下:

    public class Lib2 extends ContentProvider { @Override public boolean onCreate() { Log.e("Lib2初始化》》》》》》》","2222222222222"); return true; } }
    3、AndroidMenifest.xml配置
    <provider android:name=".Lib1" android:authorities="${applicationId}.provider" android:exported="false" /> <provider android:name=".Lib2" android:authorities="${applicationId}.provider2" android:exported="false" />
    4、结果

    启动app,运行结果如下: 那么,如果我现在有这样一个需求:Lib1要依赖于Lib2。也就是说,只有当Lib2启动初始化完成以后,才初始化Lib,那该如何操作呢?,当然,Android Startup就是为此而存在的。

    四、Android Startup

    为了解决上面的问题,Startup为我们提供了Initializer这个接口,源码如下:

    public interface Initializer<T> { /** * Initializes and a component given the application {@link Context} * * @param context The application context. */ @NonNull T create(@NonNull Context context); /** * @return A list of dependencies that this {@link Initializer} depends on. This is * used to determine initialization order of {@link Initializer}s. * <br/> * For e.g. if a {@link Initializer} `B` defines another * {@link Initializer} `A` as its dependency, then `A` gets initialized before `B`. */ @NonNull List<Class<? extends Initializer<?>>> dependencies(); } onCreate()方法中就是需要我们需要初始化的地方dependencies()方法,添加我们需要依赖的启动项。比如A需要依赖B,那么就可以在该方法中进行配置B。
    1、InitializerLib1
    public class InitializerLib1 implements Initializer<Lib1> { @NonNull @Override public Lib1 create(@NonNull Context context) { Lib1 lib1 = new Lib1(); lib1.onCreate(); return lib1; } @NonNull @Override public List<Class<? extends Initializer<?>>> dependencies() { List<Class<? extends Initializer<?>>> dependencies = new ArrayList<>(); dependencies.add(InitializerLib2.class); return dependencies; } }

    它的启动需要依赖Lib2的启动。

    2、InitializerLib2
    public class InitializerLib2 implements Initializer<Lib2> { @NonNull @Override public Lib2 create(@NonNull Context context) { Lib2 lib2 = new Lib2(); lib2.onCreate(); return lib2; } @NonNull @Override public List<Class<? extends Initializer<?>>> dependencies() { return Collections.emptyList(); } }

    它不需要依赖,在dependencies()返回空的list集合。

    3、AndroidManifest.xml配置
    <provider android:authorities="${applicationId}.androidx-startup" android:name="androidx.startup.InitializationProvider" android:exported="false" tools:node="merge"> <meta-data android:name="com.yinggu.android.nutrition.startupdemo.InitializerLib1" android:value="@string/androidx_startup"/> </provider>

    我们只配置了InitializerLib1,因为我们在它的dependencies()方法中依赖了InitializerLib2,所以就不需要配置了。看到这里,我们还是要看一下InitializationProvider

    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; } ... }

    只需要关注AppInitializer.getInstance(context).discoverAndInitialize()。

    (1)discoverAndInitialize()
    void discoverAndInitialize() { try { Trace.beginSection(SECTION_NAME); ComponentName provider = new ComponentName(mContext.getPackageName(), InitializationProvider.class.getName()); //1、找到meta-data的配置项 ProviderInfo providerInfo = mContext.getPackageManager() .getProviderInfo(provider, GET_META_DATA); Bundle metadata = providerInfo.metaData; String startup = mContext.getString(R.string.androidx_startup); if (metadata != null) { Set<Class<?>> initializing = new HashSet<>(); Set<String> keys = metadata.keySet(); for (String key : keys) { String value = metadata.getString(key, null); if (startup.equals(value)) { Class<?> clazz = Class.forName(key); if (Initializer.class.isAssignableFrom(clazz)) { //2、得到Initializer组件 Class<? extends Initializer<?>> component = (Class<? extends Initializer<?>>) clazz; if (StartupLogger.DEBUG) { StartupLogger.i(String.format("Discovered %s", key)); } // 3、开始初始化操作 doInitialize(component, initializing); } } } } } catch (PackageManager.NameNotFoundException | ClassNotFoundException exception) { throw new StartupException(exception); } finally { Trace.endSection(); } }

    以上代码,大致分为三个步骤:

    第一步:找到meta-data的配置项第二步:得到Initializer组件,这里对应InitializerLib1第二步:开始初始化组件
    (2)doInitialize()
    <T> T doInitialize( @NonNull Class<? extends Initializer<?>> component, @NonNull Set<Class<?>> initializing) { synchronized (sLock) { ... //1、实例化该组件 Object instance = component.getDeclaredConstructor().newInstance(); Initializer<?> initializer = (Initializer<?>) instance; //2、得到当前组件下的所有依赖组件 List<Class<? extends Initializer<?>>> dependencies = initializer.dependencies(); if (!dependencies.isEmpty()) { //3、递归依赖的组件 for (Class<? extends Initializer<?>> clazz : dependencies) { if (!mInitialized.containsKey(clazz)) { doInitialize(clazz, initializing); } } } if (StartupLogger.DEBUG) { StartupLogger.i(String.format("Initializing %s", component.getName())); } //4、调用组件的onCreate()方法 result = initializer.create(mContext); if (StartupLogger.DEBUG) { StartupLogger.i(String.format("Initialized %s", component.getName())); } initializing.remove(component); mInitialized.put(component, result); return (T) result; } finally { Trace.endSection(); } } }

    以上代码,大致也可分为四个步骤:

    第一步:实例化该组件第二步:得到当前组件下的所有依赖组件第三步:递归依赖的组件,继续执行doInitialize()方法第四步:调用组件的onCreate()方法

    其实在第三步我们就可以发现,不需要配置依赖组件的关键所在。

    4、结果

    没运行前,我们理想中的结果,应该是先打印Lib2的日志,再打印Lib1的日志,看看如何。 是不是很炫酷!!!

    五、补充

    以上基本上介绍完startup的用法。其实,官网上提供了两种初始化组件的方法

    自动初始化:在AndroidManifest.xml中配置启动手动初始化(懒加载):代码启动

    第一种方式,上面的代码已经介绍过了,好处就是,不需要代码管理,自动执行。 第二种方式,我们也可以尝试一下:

    AppInitializer.getInstance(getApplicationContext()).initializeComponent(InitializerLib1.class);

    一行代码搞定,我们看一下原理:

    public <T> T initializeComponent(@NonNull Class<? extends Initializer<T>> component) { return doInitialize(component, new HashSet<Class<?>>()); }

    其实,最终还是无法逃离 doInitialize() 方法。这里就不做多余的复述了。 这种启动方式,操作上其实也简单,另外还有一个好处就是,可以加快app的启动速度,因为我们不需要在启动的时候就做耗时的初始化操作。 推荐使用!!!

    Processed: 0.011, SQL: 9