IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android Jetpack套件解析(一)——Startup库分析 -> 正文阅读

[移动开发]Android Jetpack套件解析(一)——Startup库分析

简介

App Startup 库提供了一种在应用程序启动时初始化组件的简单、高效的方法。库开发人员和应用程序开发人员都可以使用 App Startup 来简化启动顺序并明确设置初始化顺序。

App Startup 允许您定义共享单个内容提供者的组件初始值设定项,而不是为您需要初始化的每个组件定义单独的内容提供程序。这可以显着缩短应用程序启动时间。

使用方法

单个Initializer

通过下面的配置在项目中引入Startup库:

dependencies {
    implementation("androidx.startup:startup-runtime:1.0.0")
}

要完成某些初始化工作,我们需要自定义一个Initializer类,比如如下代码:

package com.yb.testjetpack

import android.content.Context
import android.util.Log
import androidx.startup.Initializer

// 自定义Initializer,必须实现Initializer接口
class MyInitializer : Initializer<Unit> {
    companion object {
        private const val TAG = "MyInitializer"
    }

    // 在这里完成某些初始化工作
    override fun create(context: Context): Unit {
        Log.d(TAG, "create: do some init...")
    }

    // 该方法返回MyInitializer初始化前依赖的其他Initializer,这些Initializer会先执行
    override fun dependencies(): MutableList<Class<out Initializer<*>>> {
        return arrayListOf()
    }
}

然后在项目的AndroidManifest.xml文件中注册这个Initializer:

<provider
    android:name="androidx.startup.InitializationProvider"
    android:authorities="${applicationId}.androidx-startup"
    android:exported="false"
    tools:node="merge">
    <meta-data
        android:name="com.yb.testjetpack.MyInitializer"
        android:value="androidx.startup" />
</provider>

通过以上简单的3步即可将Startup库应用在项目中,我们运行项目时,MyInitializer类中的onCreate方法会自动得到调用。

多个Initializer

以上介绍的是项目中只有一个Initializer的情况,其实项目中也可以定义多个不同的Initializer。

在上面的代码中,我们可以发现Initializer接口中有个dependencies方法,这个方法的返回值是一个数组,代表当前Initializer的初始化依赖的其他Initializer,下面举个例子说明应用中存在多个不同Initializer时的处理方法:

假设当前应用中有三个不同的Initializer:

  • MyInitializer
  • FirstInitializer
  • SecondInitializer

这三个Initializer的初始化有一些特定的顺序:SecondInitializer的初始化依赖FirstInitializer,而FirstInitializer的初始化依赖MyInitializer,即MyInitializer最先执行,其次是FirstInitializer执行,最后是SecondInitializer,那么这三个Initializer的写法如下:

// MyInitializer
class MyInitializer : Initializer<Unit> {
    companion object {
        private const val TAG = "MyInitializer"
    }

    // 在这里完成某些初始化工作
    override fun create(context: Context): Unit {
        Log.d(TAG, "create: do some init...")
    }

    // 该方法返回MyInitializer初始化前依赖的其他Initializer,这些Initializer会先执行
    override fun dependencies(): MutableList<Class<out Initializer<*>>> {
    	// MyInitializer的初始化不依赖其他任何Initializer,返回一个空列表即可
        return arrayListOf()
    }
}

// FirstInitializer
class FirstInitializer : Initializer<String> {

    companion object {
        private const val TAG = "FirstInitializer"
    }

    override fun create(context: Context): String {
        Log.d(TAG, "create: init FirstInitializer...")
        return "FirstInitializer"
    }

    override fun dependencies(): MutableList<Class<out Initializer<*>>> {
    	// FirstInitializer的初始化依赖MyInitializer
        return arrayListOf(MyInitializer::class.java)
    }

}

// SecondInitializer
class SecondInitializer : Initializer<Unit> {

    companion object {
        private const val TAG = "SecondInitializer"
    }

    override fun create(context: Context) {
        Log.d(TAG, "create: init SecondInitializer...")
    }

    override fun dependencies(): MutableList<Class<out Initializer<*>>> {
        // SecondInitializer的初始化依赖FirstInitializer
        return arrayListOf(FirstInitializer::class.java)
    }
}

然后在AndroidManifest.xml文件中注册Initializer。

你可以将这三个Initializer都注册,像这样:

<provider
    android:name="androidx.startup.InitializationProvider"
    android:authorities="${applicationId}.androidx-startup"
    android:exported="false"
    tools:node="merge">
    <meta-data
        android:name="com.yb.testjetpack.MyInitializer"
        android:value="androidx.startup" />
    <meta-data
        android:name="com.yb.testjetpack.FirstInitializer"
        android:value="androidx.startup" />
    <meta-data
        android:name="com.yb.testjetpack.SecondInitializer"
        android:value="androidx.startup" />
</provider>

也可以只注册最后一个被调用的Initializer

比如以上三个Initializer的调用顺序是:MyInitializerFirstInitializerSecondInitializer

那么在AndroidManifest.xml文件中,注册SecondInitializer即可:

<provider
    android:name="androidx.startup.InitializationProvider"
    android:authorities="${applicationId}.androidx-startup"
    android:exported="false"
    tools:node="merge">
    <meta-data
        android:name="com.yb.testjetpack.SecondInitializer"
        android:value="androidx.startup" />
</provider>

Initializer懒加载

以上介绍的单一Initializer和多个Initializer在注册到AndroidManifest.xml文件中后,不需要在代码中显式调用,App启动时即可自动完成Initializer中onCreate方法的执行,其原理会在后面详细说明。

某些时候我们可能并不需要模块在App启动时就立刻初始化,而是希望通过调用Java代码完成初始化。

如果你只想禁用单个Initializer的自动初始化,可以这么做:

<provider
    android:name="androidx.startup.InitializationProvider"
    android:authorities="${applicationId}.androidx-startup"
    android:exported="false"
    tools:node="merge">
    <meta-data
        android:name="com.yb.testjetpack.MyInitializer"
        tools:node="remove" />
</provider>

注意meta-data中的tools:node="remove"属性,这个配置并非简单的删除条目,而是确保合并工具也从所有其他合并的清单文件中删除条目。

如果你想禁用所有的Initializer的自动初始化,可以这么做:

<provider
    android:name="androidx.startup.InitializationProvider"
    android:authorities="${applicationId}.androidx-startup"
    tools:node="remove" />

然后使用Java或Kotlin代码完成某个Initializer手动的初始化,比如下面的代码展示了在点击TextView后初始化MyInitializer:

findViewById<TextView>(R.id.text).setOnClickListener {
    AppInitializer.getInstance(this).initializeComponent(MyInitializer::class.java)
}

Startup库原理解析

你是不是和我一样好奇为啥Startup库不需要显式的执行初始化代码即可自动帮我们完成create方法中代码的执行?

通过Startup库的源码可以找到答案:Startup库使用了ContentProvider,而ContentProvider在注册到AndroidManifest.xml文件中后,ContentProvider中的onCreate方法会在Application的attachBaseContext方法之后,onCreate方法之前被调用。本篇不详细讨论ContentProvider的启动流程,仅仅解析Startup库的源码。

Startup库的源码非常少,仅有5个类,如下图所示:
在这里插入图片描述
其中Initializer是一个接口,其中定义了两个方法:create() dependencies(),源码如下:

/**
 * Initializers can be used to initialize libraries during app startup, without the need to use additional android.content.ContentProviders.
 * Type parameters:
 * <T> – The instance type being initialized
 */
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();
}

根据源码上的注释可以知道,T表示即将被初始化的实例类型。

Startup库之所以可以在App启动时自动被执行,主要的类还是InitializationProvider,这个类继承了ContentProvider,其onCreate方法源码如下:

@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();

discoverAndInitialize()方法会去AndroidManifest.xml文件中查找<meta-data>标签,并过滤出值为androidx.startup的标签,接着通过反射去实例化我们自定义的Initializer,代码如下:

void discoverAndInitialize() {
    try {
        Trace.beginSection(SECTION_NAME);
        ComponentName provider = new ComponentName(mContext.getPackageName(),
                InitializationProvider.class.getName());
        ProviderInfo providerInfo = mContext.getPackageManager()
                .getProviderInfo(provider, GET_META_DATA);
        Bundle metadata = providerInfo.metaData;
        // startup为字符串"androidx.startup"
        String startup = mContext.getString(R.string.androidx_startup);
        if (metadata != null) {
            Set<Class<?>> initializing = new HashSet<>();
            // 获取AndroidManifest.xml中所有meta-data标签
            Set<String> keys = metadata.keySet();
            for (String key : keys) {
                String value = metadata.getString(key, null);
                // 匹配到"androidx.startup",表示这个标签是自定义的Initializer
                if (startup.equals(value)) {
                	// 通过反射实例化自定义Initializer
                    Class<?> clazz = Class.forName(key);
                    if (Initializer.class.isAssignableFrom(clazz)) {
                        Class<? extends Initializer<?>> component =
                                (Class<? extends Initializer<?>>) clazz;
                        mDiscovered.add(component);
                        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();
    }
}

<T> T doInitialize(
        @NonNull Class<? extends Initializer<?>> component,
        @NonNull Set<Class<?>> initializing) {
    synchronized (sLock) {
        boolean isTracingEnabled = Trace.isEnabled();
        try {
            if (isTracingEnabled) {
                // Use the simpleName here because section names would get too big otherwise.
                Trace.beginSection(component.getSimpleName());
            }
            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();
					// 如果依赖不为空,则先初始化依赖的Initializer
                    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()));
                    }
                    // 执行自定义Initializer中的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();
        }
    }
}

写到这里你应该已经知道Startup库为甚么能自动执行初始化操作了吧,再想想我们使用LeakCanary库时,只是在build.gradle文件中添加了依赖:

debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7'

再不需要其他代码的调用,LeakCanary就能自动检测内存泄漏问题,这种隐藏式的初始化,其原理和Startup库是一样的,都使用了ContentProvider。

Startup库优点

使用Startup库在某些情况下可以加速我们App的启动过程,具体可以查看如下两篇文章:

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-07-15 16:18:51  更:2021-07-15 16:19:24 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/2 3:51:14-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码