应用启动优化—App Startup

一、简介

App Startup 是 Android Jetpack 家族的成员,提供了一种在应用程序启动时,简单、高效的初始化组件的方法。无论是App开发人员,还是Library开发人员都可以使用App Startup来简化启动过程,并显式地设置初始化顺序。

App Startup 不需要为每个组件的初始化定义单独的 ContentProvider,它提供了一个 ContentProvider 来运行所有依赖项的初始化,从而显著的提高了应用程序的启动速度。

 

二、已知手段

  1. 在 Application 中做初始化:利用异步延迟初始化,或者延迟到 Activity 中初始化。
  2. 在 ContentProvider 中做初始化:在 Application 的 onCreate 方法之前执行。
  3. 阿里巴巴开源的 Alpha:基于PERT图构建的异步启动框架。
  4. 自定义启动器 做初始化:基于有向无环图拓扑排序的启动框架。

 

三、如何使用

1. 添加依赖

implementation "androidx.startup:startup-runtime:1.0.0-alpha02"

2. 实现Initializer<T>接口

public class Sdk4Initializer implements Initializer<Sdk4> {

    @NonNull
    @Override
    public Sdk4 create(@NonNull Context context) {
        // 执行初始化逻辑
        Sdk4.getInstance().init(context);
        return Sdk4.getInstance();
    }

    @NonNull
    @Override
    public List<Class<? extends Initializer<?>>> dependencies() {
        List<Class<? extends Initializer<?>>> dependencies = new ArrayList<>();
        // 添加初始化依赖项
        dependencies.add(Sdk1Initializer.class);
        dependencies.add(Sdk2Initializer.class);
        return dependencies;
    }
}

3. 注册AndroidManifest配置

<provider
    android:name="androidx.startup.InitializationProvider"
    android:authorities="${applicationId}.androidx-startup"
    android:exported="false"
    tools:node="merge">
    <meta-data
        android:name="com.example.demo.appstartup.Sdk1Initializer"
        android:value="@string/androidx_startup" />
    <meta-data
        android:name="com.example.demo.appstartup.Sdk3Initializer"
        android:value="@string/androidx_startup"
        tools:node="remove" />
    <meta-data
        android:name="com.example.demo.appstartup.Sdk5Initializer"
        android:value="@string/androidx_startup"
        tools:node="remove" />
</provider>
  • tools:node="merge"

在没有冲突的情况下,使用合并冲突启发式算法,合并此标记中的所有属性以及所有嵌套元素。它是节点合并的默认模式。

  • tools:node="remove"

从合并后的清单中移除此元素。此标记在provider标签下,将禁用所有组件初始化;在meta-data标签下,将禁用单个组件初始化。

  • android:value="xxx"

这里必须指定为字符串androidx_startup的值,否则声明的组件不生效。

4. 延迟初始化

<meta-data
    android:name="com.example.demo.appstartup.Sdk5Initializer"
    android:value="@string/androidx_startup"
    tools:node="remove" />

// 手动执行初始化逻辑
AppInitializer.getInstance(getApplicationContext()).initializeComponent(Sdk5Initializer.class);

 

规律:

a. 不声明meta-data标签,自动初始化失效,手动初始化依然可以生效

b. 声明meta-data标签,且添加了tools:node="remove",自动初始化失效,手动初始化依然可以生效

c. 不声明meta-data标签,自动初始化失效;若做为依赖项,其他组件初始化之前,初始化依然可以生效

d. 声明meta-data标签,且添加了tools:node="remove";若做为依赖项,其他组件初始化之前,初始化依然可以生效

 

四、原理简析

1. 代码结构

2. 设计思路

  • 使用 InitializationProvider 管理多个初始化项,避免创建多个 ContentProvider 做初始化,减少初始化耗时,提升 APP 的启动速度
  • 通过实现 Initializer<T> 接口,设置初始化的依赖项,自定义组件间的初始化顺序
  • 通过 Android tools 命名空间,配置组件是否自动初始化
  • 支持延迟初始化,即手动初始化

3. 核心类

1) Initializer

public interface Initializer<T> {

    /**
     * 初始化
     */
    @NonNull
    T create(@NonNull Context context);

    /**
     * 设置依赖项,用于初始化顺序
     */
    @NonNull
    List<Class<? extends Initializer<?>>> dependencies();
}

2) InitializationProvider

public final class InitializationProvider extends ContentProvider {

    /**
     * 扫描和初始化AndroidManifest中配置的组件
     */
    @Override
    public boolean onCreate() {
        Context context = getContext();
        if (context != null) {
            AppInitializer.getInstance(context).discoverAndInitialize();
        } else {
            throw new StartupException("Context cannot be null");
        }
        return true;
    }

    ...
}

3) AppInitializer

public final class AppInitializer {

    private static final String SECTION_NAME = "Startup";

    private static volatile AppInitializer sInstance;

    private static final Object sLock = new Object();

    // 缓存已经初始化过的组件信息
    @NonNull
    final Map<Class<?>, Object> mInitialized;

    @NonNull
    final Context mContext;

    AppInitializer(@NonNull Context context) {
        mContext = context.getApplicationContext();
        mInitialized = new HashMap<>();
    }

    @NonNull
    @SuppressWarnings("UnusedReturnValue")
    public static AppInitializer getInstance(@NonNull Context context) {
        if (sInstance == null) {
            synchronized (sLock) {
                if (sInstance == null) {
                    sInstance = new AppInitializer(context);
                }
            }
        }
        return sInstance;
    }

    /**
     * 用于延迟初始化(手动初始化)指定的组件
     */
    @NonNull
    @SuppressWarnings("unused")
    public <T> T initializeComponent(@NonNull Class<? extends Initializer<T>> component) {
        return doInitialize(component, new HashSet<Class<?>>());
    }

    @NonNull
    @SuppressWarnings({"unchecked", "TypeParameterUnusedInFormals"})
    <T> T doInitialize(@NonNull Class<? extends Initializer<?>> component, @NonNull Set<Class<?>> initializing) {
        synchronized (sLock) {
            boolean isTracingEnabled = Trace.isEnabled();
            try {
                if (isTracingEnabled) {
                    Trace.beginSection(component.getSimpleName());
                }

                // initializing存储正在初始化的组件,这里为了处理循环依赖问题
                if (initializing.contains(component)) {
                    String message = String.format("Cannot initialize %s. Cycle detected.", component.getName());
                    throw new IllegalStateException(message);
                }
                Object result;

                // 避免重复初始化
                if (!mInitialized.containsKey(component)) {
                    initializing.add(component);
                    try {
                        // 构造一个组件的实例
                        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);
                                }
                            }
                        }
                        if (StartupLogger.DEBUG) {
                            StartupLogger.i(String.format("Initializing %s", component.getName()));
                        }

                        // 调用组件的create方法,执行具体的初始化逻辑
                        result = initializer.create(mContext);
                        if (StartupLogger.DEBUG) {
                            StartupLogger.i(String.format("Initialized %s", component.getName()));
                        }

                        // 更改组件的初始化状态,并缓存结果
                        initializing.remove(component);
                        mInitialized.put(component, result);
                    } catch (Throwable throwable) {
                        throw new StartupException(throwable);
                    }
                } else {
                    result = mInitialized.get(component);
                }
                return (T) result;
            } finally {
                Trace.endSection();
            }
        }
    }

    /**
     * 用于自动初始化
     */
    @SuppressWarnings("unchecked")
    void discoverAndInitialize() {
        try {
            Trace.beginSection(SECTION_NAME);

            // 读取AndroidManifest文件, 获取InitializationProvider对应的meta-data标签信息
            ComponentName provider = new ComponentName(mContext.getPackageName(), InitializationProvider.class.getName());
            ProviderInfo providerInfo = mContext.getPackageManager().getProviderInfo(provider, GET_META_DATA);
            Bundle metadata = providerInfo.metaData;
            
            // 获取androidx_startup字符串值
            String startup = mContext.getString(R.string.androidx_startup);

            // 遍历meta-data标签信息,获取Initializer实现类信息,然后进行初始化
            if (metadata != null) {
                Set<Class<?>> initializing = new HashSet<>();
                Set<String> keys = metadata.keySet();
                for (String key : keys) {
                    String value = metadata.getString(key, null);

                    // value属性的值与字符串androidx_startup的值对比,相同才进行初始化
                    if (startup.equals(value)) {
                        // 反射获取组件信息,进行初始化
                        Class<?> clazz = Class.forName(key);
                        if (Initializer.class.isAssignableFrom(clazz)) {
                            Class<? extends Initializer<?>> component = (Class<? extends Initializer<?>>) clazz;
                            if (StartupLogger.DEBUG) {
                                StartupLogger.i(String.format("Discovered %s", key));
                            }
                            doInitialize(component, initializing);
                        }
                    }
                }
            }
        } catch (PackageManager.NameNotFoundException | ClassNotFoundException exception) {
            throw new StartupException(exception);
        } finally {
            Trace.endSection();
        }
    }
}

 

参考:

https://developer.android.google.cn/topic/libraries/app-startup

https://www.jianshu.com/p/f0902a219951

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值