欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

应用启动优化—App Startup

程序员文章站 2022-07-02 09:37:44
一、简介App Startup 是 Android Jetpack 家族的成员,提供了一种在应用程序启动时,简单、高效的初始化组件的方法。无论是App开发人员,还是Library开发人员都可以使用App Startup来简化启动过程,并显式地设置初始化顺序。App Startup 不需要为每个组件的初始化定义单独的 ContentProvider,它提供了一个 ContentProvider 来运行所有依赖项的初始化,从而显著的提高了应用程序的启动速度。二、启动优化手段三、App.....

一、简介

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

应用启动优化—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. 代码结构

应用启动优化—App Startup

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

本文地址:https://blog.csdn.net/ecjtuhq/article/details/107851773