最近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補給站】或者掃描下方二維碼進行關注,固然你也能夠直接關注當前網站的賬號。主要區別就是微信可以更方法互動。
公衆號更新不會很頻繁,但一旦更新一定是純乾貨。