最近Jetpack
又添加了新成员App Startup
,官方声明这是一个在Android应用启动时,针对初始化组件进行优化的依赖库。本人第一次听到后很是高兴,由于本身负责的项目在启动时须要初始化的东西实在是太多,并且有点杂乱无章,都耦合在一块儿了。对于能够异步初始化的组件也没有进行异步处理,而对于已经处理过的异步组件它们之间的依赖关系或者多个异步以后的统一逻辑处理也没有一个很好的统一规范。因此针对这种状况早就想找个方案来优化了,此次终于等到了App Startup
。java
可是,当我元气满满的去查看官方文档时,并无找到预想中的结果。官方文档中只提到了能够经过一个ContentProvider
来统一管理须要初始化的组件,同时经过dependencies()
方法解决组件间初始化的依赖顺序,而后呢?没了?等等官方你是否是漏了什么?android
异步处理呢?虽然咱们能够在create()
方法中手动建立子线程进行异步任务,但一个异步任务依赖另外一个异步任务又该如何处理呢?多个异步任务完成以后,统一逻辑处理又在哪里呢?依赖任务完成后的回调又在哪里?亦或者是依赖任务完成后的通知?git
我有点不相信,因此又去查看了App Startup
的源码,源码很简单,也就几个文件,最后发现确实只支持上面的那几个功能。github
若是你的项目都是同步初始化的话,而且使用到了多个ContentProvider
,App Startup
可能有必定的优化空间,毕竟统一到了一个ContentProvider
中,同时支持了简单的顺序依赖。segmentfault
值得一提的是,App Startup
中只提供了使用反射来获取初始化的组件实例,这对于一些没有过多依赖的初始化项目来讲,盲目使用App Startup
来优化是否会对启动速度进一步形成影响呢?微信
因此细想了一下,不由让我想起了三国时的一个名词:鸡肋
。食之无味,弃之惋惜。架构
但最终我仍是决定放弃使用它。app
放弃以后有点不甘心,可能更多的是它没有解决我当前的项目场景。都分析了这么多,源码都看了,总不能半途而废吧,因此本身咬咬牙再补充一点呗。异步
因此坚持一下,就有了下面这个库,App Startup
的进阶版Android Startup
。ide
Android Startup提供一种在应用启动时可以更加简单、高效的方式来初始化组件。开发人员可使用Android Startup
来简化启动序列,并显式地设置初始化顺序与组件之间的依赖关系。
与此同时,Android Startup
支持同步与异步等待,并经过有向无环图拓扑排序的方式来保证内部依赖组件的初始化顺序。
因为Android Startup
是基于App Startup
进行的扩展,因此它的使用方式与App Startup
有点相似,该有的功能基本上都有,同时额外还附加其它功能。
下面是一张与google的App Startup功能对比的表格。
指标 | App Startup | Android Startup |
---|---|---|
手动配置 | ✅ | ✅ |
自动配置 | ✅ | ✅ |
依赖支持 | ✅ | ✅ |
闭环处理 | ✅ | ✅ |
线程控制 | ❌ | ✅ |
异步等待 | ❌ | ✅ |
依赖回调 | ❌ | ✅ |
拓扑优化 | ❌ | ✅ |
下面简单介绍一下Android Startup
的使用。
将下面的依赖添加到build.gradle
文件中:
dependencies { implementation 'com.rousetime.android:android-startup:1.0.1' }
依赖版本的更新信息: Release
android-startup提供了两种使用方式,在使用以前须要先定义初始化的组件。
每个初始化的组件都须要实现AndroidStartup<T>
抽象类,它实现了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
,因此主线程会阻塞等待直到它执行完毕。
例如,你还定义了SampleThirdStartup与SampleFourthStartup
第一种初始化方法是在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>
你不须要将SampleFirstStartup
、SampleSecondStartup
与SampleThirdStartup
添加到<meta-data>
标签中。这是由于在SampleFourthStartup
中,它的dependencies()
中依赖了这些组件。StartupProvider
会自动识别已经声明的组件中依赖的其它组件。
第二种初始化方法是在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() } }
若是你开启了日志输出,而后运行项目以后,将会在控制台中输出通过拓扑排序优化以后的初始化组件的执行顺序。
D/StartupTrack: TopologySort result: ================================================ ordering start ================================================ order [0] Class: SampleFirstStartup => Dependencies size: 0 => callCreateOnMainThread: true => waitOnMainThread: false order [1] Class: SampleSecondStartup => Dependencies size: 1 => callCreateOnMainThread: false => waitOnMainThread: true order [2] Class: SampleThirdStartup => Dependencies size: 2 => callCreateOnMainThread: false => waitOnMainThread: false order [3] Class: SampleFourthStartup => Dependencies size: 3 => callCreateOnMainThread: false => waitOnMainThread: false ================================================ ordering end ================================================
完整的代码实例,你能够经过查看app获取。
LoggerLevel
: 控制Android Startup中的日志输出,可选值包括LoggerLevel.NONE
, LoggerLevel.ERROR
and LoggerLevel.DEBUG
。AwaitTimeout
: 控制Android Startup中主线程的超时等待时间,即阻塞的最长时间。使用这些配置,你须要定义一个类去实现StartupProviderConfig
接口,而且实现它的对应方法。
class SampleStartupProviderConfig : StartupProviderConfig { override fun getConfig(): StartupConfig = StartupConfig.Builder() .setLoggerLevel(LoggerLevel.DEBUG) .setAwaitTimeout(12000L) .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须要借助StartupManager.Builder()
进行配置。
override fun onCreate() { super.onCreate() val config = StartupConfig.Builder() .setLoggerLevel(LoggerLevel.DEBUG) .setAwaitTimeout(12000L) .build() StartupManager.Builder() .setConfig(config) ... .build(this) .start() .await() }
createExecutor(): Executor
: 若是定义的组件没有运行在主线程,那么能够经过该方法进行控制运行的子线程。onDependenciesCompleted(startup: Startup<*>, result: Any?)
: 该方法会在每个依赖执行完毕以后进行回调。AwesomeGithub中使用了Android Startup,优化配置的初始化时间与组件化开发的配置注入时机,使用前与使用后时间对比:
状态 | 启动页面 | 消耗时间 |
---|---|---|
使用前 | WelcomeActivity | 420ms |
使用后 | WelcomeActivity | 333ms |
AwesomeGithub是基于Github的客户端,纯练习项目,支持组件化开发,支持帐户密码与认证登录。使用Kotlin语言进行开发,项目架构是基于JetPack&DataBinding的MVVM;项目中使用了Arouter、Retrofit、Coroutine、Glide、Dagger与Hilt等流行开源技术。
除了Android原生版本,还有基于Flutter的跨平台版本flutter_github。
若是你喜欢个人文章,你能够关注个人微信公众号:【Android补给站】或者扫描下方二维码进行关注,固然你也能够直接关注当前网站的账号。主要区别就是微信可以更方法互动。
公众号更新不会很频繁,但一旦更新一定是纯干货。