Jetpack-Startup源码解析

Startup源码解析

源码版本:

  • Startup:1.1.0

导航:

使用

实现Initializer

class WorkManagerInitializer : Initializer<WorkManager> {
    
    override fun create(context: Context): WorkManager {
        // 初始化WorkManager
        val configuration = Configuration.Builder().build()
        WorkManager.initialize(context, configuration)
        // 返回WorkManager初始化后的实例,以便通过AppInitializer.getInstance(context).initializeComponent(WorkManagerInitializer::class.java)初始化并获取。
        return WorkManager.getInstance(context)
    }
    
    override fun dependencies(): List<Class<out Initializer<*>>> {
        // 无依赖其它库
        return emptyList()
    }
}

因为WorkManager不依赖于任何其它库,所以该dependencies()方法返回一个空列表

class ExampleLoggerInitializer : Initializer<ExampleLogger> {
    override fun create(context: Context): ExampleLogger {
        // 初始化ExampleLogger,并返回其对象,以便通过AppInitializer.getInstance(context).initializeComponent(ExampleLoggerInitializer::class.java)初始化并获取。
        return ExampleLogger(WorkManager.getInstance(context))
    }

    override fun dependencies(): List<Class<out Initializer<*>>> {
        // 依赖WorkManagerInitializer
        return listOf(WorkManagerInitializer::class.java)
    }
}

因为ExampleLogger依赖于WorkManager,所以该dependencies()方法返回包含WorkManagerInitializer列表,使其在它之前初始化

初始化Initializer

自动初始化组件

在清单文件中添加

<provider
    android:name="androidx.startup.InitializationProvider"
    android:authorities="${applicationId}.androidx-startup"
    android:exported="false"
    tools:node="merge">
    <meta-data  
        android:name="com.example.ExampleLoggerInitializer" // 要初始化的Initializer类名全路径
        android:value="androidx.startup" />
</provider>

自动初始化组件,其为在应用启动时初始化组件。不需要为WorkManagerInitializer添加<meta-data>条目,因为它是ExampleLoggerInitializer依赖项,当然声明了也不会有问题(因为已经被初始了,则不会再进行初始化了,详细看源码介绍)。

说明:

  1. provider声明格式固定,只需要修改<meta-data>name即可。
  2. <meta-data>顺序,决定着初始化的顺序
  3. tools:node="merge"属性确保清单合并工具正确解决任何冲突条目

手动初始化组件

手动初始化组件,其意为就是不自动初始化组件,所以您必须禁用自动初始化,其又称为延迟初始化

禁用单个组件的自动初始化

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

禁用单个组件的自动初始化,将tools:node="remove"声明到指定<meta-data>上即可,其它保持不变。

说明:

  1. 如果<meta-data>是自己的(可修改),则删除指定的<meta-data>即可;如果<meta-data>不是自己的(如三方库,不可修改),则使用tools:node="remove"声明即可。
  2. 禁用组件的自动初始化也会禁用该组件的依赖项自动初始化
  3. tools:node="remove",从合并后的清单移除此元素。

禁用所有组件的自动初始化

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

禁用所有组件的自动初始化,将tools:node="remove"声明到InitializationProviderprovider上即可,其它保持不变或删除掉全部<meta-data>都可以。

说明:

  1. 不推荐禁用所有组件的自动初始化,因为会禁用掉所有使用startup初始化三方库(如lifecycle库的ProcessLifecycleInitializer),导致得需要手动初始化所有使用startup库的三方库,不方便后续维护。

手动初始化组件

val result = AppInitializer.getInstance(context)
    .initializeComponent(ExampleLoggerInitializer::class.java)

手动初始化组件,调用AppInitializer.initializeComponent()方法即可,以上代码手动初始化ExampleLogger,又因为ExampleLogger依赖WorkManager,所以WorkManager也完成了初始化。

说明:

  1. 其返回值resultExampleLoggerInitializercreate()方法返回的ExampleLogger实例。
  2. 如果AppInitializer.initializeComponent()方法初始化的Initializer已经初始化完成,再次调用则不会再次初始化,而是会返回第一次初始化结果值(如初始化ExampleLoggerInitializer,始终会返回第一次初始化ExampleLogger实例)。

源码

实现Initializer

Initializer类

public interface Initializer<T> {

    // 给定应用程序上下文初始化组件
    @NonNull
    T create(@NonNull Context context);

    // 这个Initializer依赖的依赖项列表。这是用来确定Initializer的初始化顺序。
    // 例如,如果一个初始化器 B 定义了另一个初始化器 A 作为它的依赖,那么 A 在 B 之前被初始化。
    @NonNull
    List<Class<? extends Initializer<?>>> dependencies();
}

Initializer类,为初始化器接口,这个接口定义了两个重要的方法:

  • create()方法,它包含初始化组件所需的所有操作,并返回T的实例
  • dependencies()方法,它返回Initializer所依赖的其它Initializer<T>对象的列表。您可以使用此方法控制app启动时运行initializers顺序

初始化Initializer

自动初始化组件

我们先来看一下startup库的清单文件

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="androidx.startup" >

    <uses-sdk
        android:minSdkVersion="14"
        android:targetSdkVersion="30" />

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

</manifest>

里面声明了最小SDK版本14,以及声明了一个ContentProvider,它是InitializationProvider,接下来我们再来看一下InitializationProvider类。

InitializationProvider类

public class InitializationProvider extends ContentProvider {
    @Override
    public final boolean onCreate() {
        Context context = getContext();
        if (context != null) {
            AppInitializer.getInstance(context).discoverAndInitialize();
        } else {
            throw new StartupException("Context cannot be null");
        }
        return true;
    }

    @Nullable
    @Override
    public final Cursor query(
            @NonNull Uri uri,
            @Nullable String[] projection,
            @Nullable String selection,
            @Nullable String[] selectionArgs,
            @Nullable String sortOrder) {
        throw new IllegalStateException("Not allowed.");
    }

    @Nullable
    @Override
    public final String getType(@NonNull Uri uri) {
        throw new IllegalStateException("Not allowed.");
    }

    @Nullable
    @Override
    public final Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        throw new IllegalStateException("Not allowed.");
    }

    @Override
    public final int delete(
            @NonNull Uri uri,
            @Nullable String selection,
            @Nullable String[] selectionArgs) {
        throw new IllegalStateException("Not allowed.");
    }

    @Override
    public final int update(
            @NonNull Uri uri,
            @Nullable ContentValues values,
            @Nullable String selection,
            @Nullable String[] selectionArgs) {
        throw new IllegalStateException("Not allowed.");
    }
}

InitializationProvider类,它是一个ContentProvider,在其onCreate()方法里面调用了AppInitializer.getInstance(context).discoverAndInitialize(),我们先来看一下AppInitializer.getInstance(context)方法,然后再看一下其discoverAndInitialize()方法。

说明:

  1. ApplicationContentProviderActivityonCreate()执行顺序:Application.attachBaseContext() -> ContentProvider.onCreate() -> Application.onCreate() -> Activity.onCreate()

AppInitializer --> getInstance方法

public final class AppInitializer {

    // 单例AppInitializer实例
    private static volatile AppInitializer sInstance;
    
    // 获取单例AppInitializer的实例
    @NonNull
    @SuppressWarnings("UnusedReturnValue")
    public static AppInitializer getInstance(@NonNull Context context) {
        if (sInstance == null) {
            synchronized (sLock) {
                if (sInstance == null) {
                    sInstance = new AppInitializer(context);
                }
            }
        }
        return sInstance;
    }

}

AppInitializer.getInstance(context)方法,为单例获取AppInitializer的实例。

AppInitializer --> discoverAndInitialize方法

public final class AppInitializer {

    // Tracing
    private static final String SECTION_NAME = "Startup";

    // 已发现Initializer的Set集合,value为Initializer的class。
    @NonNull
    final Set<Class<? extends Initializer<?>>> mDiscovered;   

    // 发现并初始化
    @SuppressWarnings("unchecked")
    void discoverAndInitialize() {
        try {
            Trace.beginSection(SECTION_NAME);
            // 获取InitializationProvider的ProviderInfo
            ComponentName provider = new ComponentName(mContext.getPackageName(),
                    InitializationProvider.class.getName());
            ProviderInfo providerInfo = mContext.getPackageManager()
                    .getProviderInfo(provider, GET_META_DATA);
            // 获取<meta-data>信息
            Bundle metadata = providerInfo.metaData;
            // 获取字符串,值为"androidx.startup"
            String startup = mContext.getString(R.string.androidx_startup);
            // 判断metadata为不为null,即有没有配置<meta-data>,没配置则不处理。
            if (metadata != null) {
                // 初始化中Initializer的Class集合
                Set<Class<?>> initializing = new HashSet<>();
                // 获取metadata中所有的key,即获取所有<meta-data>内的android:name。
                Set<String> keys = metadata.keySet();
                // 遍历metadata中所有的key
                for (String key : keys) {
                    // 获取metadata中的值,即获取<meta-data>内的android:value。
                    String value = metadata.getString(key, null);
                    // 判断<meta-data>内的android:value为"androidx.startup"
                    if (startup.equals(value)) {
                        // 获取<meta-data>内的android:name指定的class
                        Class<?> clazz = Class.forName(key);
                        // 判断<meta-data>内的android:name指定的class是否是Initializer的子类。
                        if (Initializer.class.isAssignableFrom(clazz)) {
                            // 是Initializer子类,则强转。
                            Class<? extends Initializer<?>> component =
                                    (Class<? extends Initializer<?>>) clazz;
                            // 添加到mDiscovered(已发现)集合
                            mDiscovered.add(component);
                            // 打印日志
                            if (StartupLogger.DEBUG) {
                                StartupLogger.i(String.format("Discovered %s", key));
                            }
                            // 初始化component,即初始化<meta-data>内的android:name指定的class。
                            doInitialize(component, initializing);
                        }
                    }
                }
            }
        } catch (PackageManager.NameNotFoundException | ClassNotFoundException exception) {
            throw new StartupException(exception);
        } finally {
            Trace.endSection();
        }
    }
}

AppInitializer.discoverAndInitialize()方法,为发现并初始化Initializer,找到清单文件配置的所有Initializer,然后调用doInitialize()方法进行初始化操作。

说明:

  1. <meta-data>内的android:value,必须为androidx.startup
  2. <meta-data>内的android:name,必须为Initializer子类类名全路径
  3. 清单文件配置的所有Initializer,都会添加到mDiscovered(已发现)集合

我们再来看一下AppInitializer.doInitialize()方法。

AppInitializer --> doInitialize方法

public final class AppInitializer {
    
    // 线程锁
    private static final Object sLock = new Object();

    // 已经初始化Initializer的Map集合,key为Initializer的class,value为Initializer.onCreate()方法的返回值。
    @NonNull
    final Map<Class<?>, Object> mInitialized;

    // 做初始化
    @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) {
                    // 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)) {
                    // 没初始化过,则进行初始化,并记录其create的结果。
                    // 添加到正在初始化中的集合,标记正在初始化中。
                    initializing.add(component);
                    try {
                        // 反射创建对象
                        Object instance = component.getDeclaredConstructor().newInstance();
                        // 强转,因为component实现了Initializer,所以没问题。
                        Initializer<?> initializer = (Initializer<?>) instance;
                        // 获取其依赖的Initializer列表
                        List<Class<? extends Initializer<?>>> dependencies =
                                initializer.dependencies();
                        // 如果其依赖的Initializer列表不为空,就先初始化列表。
                        if (!dependencies.isEmpty()) {
                            // 遍历其依赖的Initializer列表
                            for (Class<? extends Initializer<?>> clazz : dependencies) {
                                // 判断其依赖的Initializer,是否已经被初始化过。
                                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);
                        // 存入已经初始化的Initializer,以及其create()方法返回的结果。
                        mInitialized.put(component, result);
                    } catch (Throwable throwable) {
                        throw new StartupException(throwable);
                    }
                } else {
                    // 已经初始化过,则获取其create()的结果。
                    result = mInitialized.get(component);
                }
                // 返回initializer.create()的结果
                return (T) result;
            } finally {
                Trace.endSection();
            }
        }
    }

}

AppInitializer.doInitialize()方法,为真正的初始化Initializer的方法,它反射创建Initializer对象,并调用其create()方法通知内部的初始化,并返回create()方法的返回值

说明:

  1. 如果这个Initializer未被初始化,则反射创建这个Initializer对象,并调用其create()方法通知内部的初始化,并将其create()方法的结果添加到在mInitialized中以便后续获取;否则,则从mInitialized中获取第一次初始化结果值,使其不会频繁创建这个Initializer对象。
  2. 如果要创建的Initializer,依赖其它Initializer,则会进行循环递归初始化其它Initializer,使其它Initializer全部初始化完成,才会执行此Initializercreate()方法。
  3. doInitialize()方法,因为使用了synchronized代码块,且锁sLock静态的(唯一),所以当有线程正在执行doInitialize()方法时,其它线程再执行doInitialize()方法时都得等待上个线程执行完成

手动初始化组件

AppInitializer --> initializeComponent方法

// 初始化Initializer类
@NonNull
@SuppressWarnings("unused")
public <T> T initializeComponent(@NonNull Class<? extends Initializer<T>> component) {
    return doInitialize(component, new HashSet<Class<?>>());
}

AppInitializer.initializeComponent()方法,直接调用了doInitialize()方法进行创建,并返回了其doInitialize()方法的结果值(即Initializer.create()方法的返回值)。

说明:

  1. 只有initializeComponent()方法才能拿到Initializercreate()方法的返回值,不管是自动初始化ContentProvider存入,还是手动初始化(调用initializeComponent()存入,都可以再次调用initializeComponent()方法获取到其在create()方法的返回值
  2. 由于doInitialize()方法,使用了synchronized,导致其执行中,其它都得等待。例如:AB耗时所以分别在一个子线程执行initializeComponent()方法,C主线程执行,使其三个分别在三个线程执行,以达到并发的效果,但是结果为:A执行中,B等待A执行完成,C等待AB执行完成,未达到并发效果。

其它源码

AppInitializer --> isEagerlyInitialized方法

// Initializer是否是被急切地初始化
public boolean isEagerlyInitialized(@NonNull Class<? extends Initializer<?>> component) {
    // 如果从未调用过discoverAndInitialize(),则没有急于初始化任何内容。
    return mDiscovered.contains(component);
}

AppInitializer.isEagerlyInitialized()方法,为Initializer是否是被急切地初始化,即是否是在清单文件配置中

优缺点

优点

  • 提供了一个规则,可以让所有的三方库,使用同一个ContentProvider在其库内部进行初始化减少了初始化代码优化创建多个ContentProvider造成的性能、时间损耗
  • 支持初始化的顺序以及依赖关系

缺点

  • 不支持多线程并发初始化。
  • 反射创建Initializer类,对性能有稍微影响。

总结

以上就是全面的Jetpack-Startup源码了!之后会出Jetpack其它源码系列,请及时关注。如果你有什么问题,大家评论区见!

最后推荐一下我的网站,开发者的技术博客: devbolg.cn ,目前包含android相关的技术,之后会面向全部开发者,欢迎大家来体验!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容