/android-startup

🔥The Android Startup library provides a straightforward, performant way to initialize components at application startup. Both library developers and app developers can use Android Startup to streamline startup sequences and explicitly set the order of initialization

Primary LanguageKotlinApache License 2.0Apache-2.0

中文|English

android-startup

Author Platform API Language Release Code Size License

Android Startup提供一种在应用启动时能够更加简单、高效的方式来初始化组件。开发人员可以使用Android Startup来简化启动序列,并显式地设置初始化顺序与组件之间的依赖关系。 与此同时,Android Startup支持同步与异步等待,并通过有向无环图拓扑排序的方式来保证内部依赖组件的初始化顺序。

下面是一张与google的App Startup功能对比的表格。

指标 App Startup Android Startup
手动配置
自动配置
依赖支持
闭环处理
线程控制
异步等待
依赖回调
拓扑优化

开源不易,希望朋友小手一抖,右上角来个star,感谢🙏

添加依赖

将下面的依赖添加到build.gradle文件中:

dependencies {
    implementation 'com.rousetime.android:android-startup:1.0.2'
}

依赖版本的更新信息: Release

快速使用

android-startup提供了两种使用方式,在使用之前需要先定义初始化的组件。

定义初始化的组件

每一个初始化的组件都需要实现AndroidStartup抽象类,它实现了Startup<T>接口,它主要有以下四个抽象方法:

  • callCreateOnMainThread(): Boolean用来控制create()方法调时所在的线程,返回true代表在主线程执行。

  • waitOnMainThread(): Boolean用来控制当前初始化的组件是否需要在主线程进行等待其完成。如果返回true,将在主线程等待,并且阻塞主线程。

  • create(): T?组件初始化方法,执行需要处理的初始化逻辑,支持返回一个T类型的实例。

  • dependencies(): List<Class<out Startup<*>>>?返回Startup<*>类型的list集合。用来表示当前组件在执行之前需要依赖的组件。

例如,下面定义一个SampleFirstStartup类来实现AndroidStartup<String>抽象类:

class SampleFirstStartup : AndroidStartup<String>() {

    override fun callCreateOnMainThread(): Boolean = true

    override fun waitOnMainThread(): Boolean = false

    override fun create(context: Context): String? {
        // todo something
        return this.javaClass.simpleName
    }

    override fun dependencies(): List<Class<out Startup<*>>>? {
        return null
    }

}

因为SampleFirstStartup在执行之前不需要依赖其它组件,所以它的dependencies()方法可以返回空,同时它会在主线程中执行。

注意:️虽然waitOnMainThread()返回了false,但由于它是在主线程中执行,而主线程默认是阻塞的,所以callCreateOnMainThread()返回true时,该方法设置将失效。

假设你还需要定义SampleSecondStartup,它依赖于SampleFirstStartup。这意味着在执行SampleSecondStartup之前SampleFirstStartup必须先执行完毕。

class SampleSecondStartup : AndroidStartup<Boolean>() {

    override fun callCreateOnMainThread(): Boolean = false

    override fun waitOnMainThread(): Boolean = true

    override fun create(context: Context): Boolean {
        // 模仿执行耗时
        Thread.sleep(5000)
        return true
    }

    override fun dependencies(): List<Class<out Startup<*>>>? {
        return listOf(SampleFirstStartup::class.java)
    }

}

dependencies()方法中返回了SampleFirstStartup,所以它能保证SampleFirstStartup优先执行完毕。 它会在子线程中执行,但由于waitOnMainThread()返回了true,所以主线程会阻塞等待直到它执行完毕。

例如,你还定义了SampleThirdStartupSampleFourthStartup

Manifest中自动配置

第一种初始化方法是在Manifest中进行自动配置。

在Android Startup中提供了StartupProvider类,它是一个特殊的content provider,提供自动识别在manifest中配置的初始化组件。 为了让其能够自动识别,需要在StartupProvider中定义<meta-data>标签。其中的name为定义的组件类,value的值对应为android.startup

<provider
    android:name="com.rousetime.android_startup.provider.StartupProvider"
    android:authorities="${applicationId}.android_startup"
    android:exported="false">

    <meta-data
        android:name="com.rousetime.sample.startup.SampleFourthStartup"
        android:value="android.startup" />

</provider>

你不需要将SampleFirstStartupSampleSecondStartupSampleThirdStartup添加到<meta-data>标签中。这是因为在SampleFourthStartup中,它的dependencies()中依赖了这些组件。StartupProvider会自动识别已经声明的组件中依赖的其它组件。

Application中手动配置

第二种初始化方法是在Application进行手动配置。

手动初始化需要使用到StartupManager.Builder()

例如,如下代码使用StartupManager.Builder()进行初始化配置。

class SampleApplication : Application() {

    override fun onCreate() {
        super.onCreate()
        StartupManager.Builder()
            .addStartup(SampleFirstStartup())
            .addStartup(SampleSecondStartup())
            .addStartup(SampleThirdStartup())
            .addStartup(SampleFourthStartup())
            .build(this)
            .start()
            .await()
    }
}

完整的示例代码,你可以通过查看app获取。

运行示例代码,控制台将会产生如下日志:

  1. 排序优化之后的初始化顺序
*****/com.rousetime.sample D/StartupTrack: TopologySort result:
    |================================================================
    |         order          |    [1]
    |----------------------------------------------------------------
    |        Startup         |    SampleFirstStartup
    |----------------------------------------------------------------
    |   Dependencies size    |    0
    |----------------------------------------------------------------
    | callCreateOnMainThread |    true
    |----------------------------------------------------------------
    |    waitOnMainThread    |    false
    |================================================================
    |         order          |    [2]
    |----------------------------------------------------------------
    |        Startup         |    SampleSecondStartup
    |----------------------------------------------------------------
    |   Dependencies size    |    1
    |----------------------------------------------------------------
    | callCreateOnMainThread |    false
    |----------------------------------------------------------------
    |    waitOnMainThread    |    true
    |================================================================
    |         order          |    [3]
    |----------------------------------------------------------------
    |        Startup         |    SampleThirdStartup
    |----------------------------------------------------------------
    |   Dependencies size    |    2
    |----------------------------------------------------------------
    | callCreateOnMainThread |    false
    |----------------------------------------------------------------
    |    waitOnMainThread    |    false
    |================================================================
    |         order          |    [4]
    |----------------------------------------------------------------
    |        Startup         |    SampleFourthStartup
    |----------------------------------------------------------------
    |   Dependencies size    |    3
    |----------------------------------------------------------------
    | callCreateOnMainThread |    false
    |----------------------------------------------------------------
    |    waitOnMainThread    |    false
    |================================================================
  1. 各组件初始化所消耗的时间
*****/com.rousetime.sample D/StartupTrack: startup cost times detail:
    |=================================================================
    |      Startup Name       |   SampleFirstStartup
    | ----------------------- | --------------------------------------
    |   Call On Main Thread   |   true
    | ----------------------- | --------------------------------------
    |   Wait On Main Thread   |   false
    | ----------------------- | --------------------------------------
    |       Cost Times        |   0 ms
    |=================================================================
    |      Startup Name       |   SampleSecondStartup
    | ----------------------- | --------------------------------------
    |   Call On Main Thread   |   false
    | ----------------------- | --------------------------------------
    |   Wait On Main Thread   |   true
    | ----------------------- | --------------------------------------
    |       Cost Times        |   5001 ms
    |=================================================================
    |      Startup Name       |   SampleThirdStartup
    | ----------------------- | --------------------------------------
    |   Call On Main Thread   |   false
    | ----------------------- | --------------------------------------
    |   Wait On Main Thread   |   false
    | ----------------------- | --------------------------------------
    |       Cost Times        |   3007 ms
    |=================================================================
    |      Startup Name       |   SampleFourthStartup
    | ----------------------- | --------------------------------------
    |   Call On Main Thread   |   false
    | ----------------------- | --------------------------------------
    |   Wait On Main Thread   |   false
    | ----------------------- | --------------------------------------
    |       Cost Times        |   102 ms
    |=================================================================
    | Total Main Thread Times |   5008 ms
    |=================================================================

更多

可选配置

  • LoggerLevel: 控制Android Startup中的日志输出,可选值包括LoggerLevel.NONE, LoggerLevel.ERROR and LoggerLevel.DEBUG

  • AwaitTimeout: 控制Android Startup中主线程的超时等待时间,即阻塞的最长时间。

  • StartupListener: Android Startup监听器,所有组件初始化完成之后该监听器会被调用。

Manifest中配置

使用这些配置,你需要定义一个类去实现StartupProviderConfig接口,并且实现它的对应方法。

class SampleStartupProviderConfig : StartupProviderConfig {

    override fun getConfig(): StartupConfig =
        StartupConfig.Builder()
            .setLoggerLevel(LoggerLevel.DEBUG)
            .setAwaitTimeout(12000L)
            .setListener(object : StartupListener {
                override fun onCompleted(totalMainThreadCostTime: Long, costTimesModels: List<CostTimesModel>) {
                    // can to do cost time statistics.
                }
            })
            .build()
}

与此同时,你还需要在manifest中进行配置StartupProviderConfig

<provider
    android:name="com.rousetime.android_startup.provider.StartupProvider"
    android:authorities="${applicationId}.android_startup"
    android:exported="false">

    <meta-data
        android:name="com.rousetime.sample.startup.SampleStartupProviderConfig"
        android:value="android.startup.provider.config" />

</provider>

经过上面的配置,StartupProvider会自动解析SampleStartupProviderConfig

Application中配置

在Application需要借助StartupManager.Builder()进行配置。

override fun onCreate() {
    super.onCreate()

    val config = StartupConfig.Builder()
        .setLoggerLevel(LoggerLevel.DEBUG)
        .setAwaitTimeout(12000L)
        .setListener(object : StartupListener {
            override fun onCompleted(totalMainThreadCostTime: Long, costTimesModels: List<CostTimesModel>) {
                // can to do cost time statistics.
            }
        })
        .build()

    StartupManager.Builder()
        .setConfig(config)
        ...
        .build(this)
        .start()
        .await()
}
  • createExecutor(): Executor: 如果定义的组件没有运行在主线程,那么可以通过该方法进行控制运行的子线程。

  • onDependenciesCompleted(startup: Startup<*>, result: Any?): 该方法会在每一个依赖执行完毕之后进行回调。

  • hadInitialized(zClass: Class<out Startup<*>>): 检验对应的组件是否已经初始化完成。

  • obtainInitializedResult(zClass: Class<out Startup<*>>): T?: 获取对应已经初始化的组件所返回的结果。

  • remove(zClass: Class<out Startup<*>>): 清除对应组件的初始化缓存结果。

  • clear(): 清除所有组件初始化的缓存结果。

实战测试

AwesomeGithub中使用了Android Startup,优化配置的初始化时间与组件化开发的配置注入时机,使用前与使用后时间对比:

状态 启动页面 消耗时间
使用前 WelcomeActivity 420ms
使用后 WelcomeActivity 333ms

License

请查看LICENSE