jetpack之App Startup

一、前言

最近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的启动速度,因为我们不需要在启动的时候就做耗时的初始化操作。 推荐使用!!!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值