sunflower
sunflower是什么?太陽花?Google沒出這個框架吧。
其實,sunflower是Google基于Google開發框架,加入大量Android JetPack組件的示例項目,涉及到kotlin,kotlin協程,新fragment,hilt,DataSource,paging3等等等等。
話不多說,先clone一下吧,地址如下:https://github.com/android/sunflower.git
開始
clone完成之后,打開項目,項目本身并不復雜,也沒有使用插件化,組件化之類的技術。
在編譯項目的時候,發生了gradle版本錯誤,因為項目默認的gradle版本是4.1.0,我的android studio還是4.0,通過升級android studio或者將gradle版本改成4.0.0解決。
常規操作,打開AndroidManifest文件,很簡單的配置文件,找到首頁 GardenActivity 。 同樣,極簡的代碼,一點點看。
兩個知識點出現了,DataBinding和Hit。一個個來,先Hilt
Hilt
Hilt?這是什么,遇到不認識的單詞,我們一般做的是放到翻譯網站翻譯一下,主流翻譯的結果是劍柄,刀柄。還是不明覺厲,此時,我們注意到hilt的包的路徑,是不是很熟悉,dagger,依賴注入?dagger中文意思是匕首,而hilt是刀柄,是不是有點明確了,光有刀還不行,還需要一個可以更安全的使用刀,而且還能保護自己安全的刀把。
那么,先下個結論,hilt的職責是依賴注入,而且是更方便,更安全的依賴注入。
引入
-
在根目錄的build.gradle添加hilt-android-gradle-plugin插件
buildscript { ... dependencies { ... classpath 'com.google.dagger:hilt-android-gradle-plugin:2.28-alpha' } }
-
在項目的build.gradle中添加依賴
apply plugin: 'kotlin-kapt' apply plugin: 'dagger.hilt.android.plugin' dependencies { .... implementation "com.google.dagger:hilt-android:2.28-alpha" kapt "com.google.dagger:hilt-android-compiler:2.28-alpha" }
- 如果不用Java8,引入的過程到這就結束了
使用
-
需要新建application,加入@HiltAndroidApp注解,隨后將其添加到配置文件中
@HiltAndroidApp class BaseApplication : Application()
-
接下來就是剛剛提到的GardenActivity了,在代碼中出現了@AndroidEntryPoint注解,Hilt總共支持六種Android類,除了剛剛出現過得Application和Activity,還有Fragment,View,Service,BroadcastService。
我們以Activity為例,簡單看一下具體的工作流程。新建一個Activity,添加@AndroidEntryPoint注解。
@AndroidEntryPoint class HiltMainActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) } }
- 此時,是不是很納悶,然后呢,說好的依賴注入呢,注入的對象呢?
-
添加注入的對象,此處以運送貨物為例,創建Truck類
class Truck { fun deliver() { Log.d(Util.TAG,"delivering") } }
-
將truck注入到Activity中
@AndroidEntryPoint class MainActivity : AppCompatActivity() { @Inject lateinit var truck: Truck override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) truck.deliver() } }
出現了另一個注解@Inject,這個注解的作用就是將truck注入到Activity中,而且MainActivity并沒有實例化Truck的代碼,但是能夠調用Truck的deliver方法,一個詞---方便。
-
如果就這樣,嘗試運行的話,就會報錯了。還需要做一些改變,Truck類也是需要@Inject修飾的
class Truck @Inject constructor() { fun deliver() { Log.d(Util.TAG,"delivering") } }
- 在主構造函數之前加入@Inject注解,此舉是告訴Hilt,Truck實例是如何產生的,在主構造函數之前加入@Inject,是告訴Hilt,Truck的實例是通過主構造函數產生的。
- 修改之后運行,成功運行
帶參數注入
在運輸過程中,肯定是需要一個司機的,那么就把司機當做參數傳入吧,稍作修改
class Truck @Inject constructor(val driver: Driver) { fun deliver() { Log.d(Util.TAG,"delivering..., the driver is $driver") } }
-
此時,hilt還不知道Driver是如何實例化的話,那么,我們就按照之前的格式在Driver類中加入@Inject
class Driver @Inject constructor()
運行代碼之后,結果如下:
可見,成功打印出了driver的身份。
Hilt和Retrofit
以上是Hilt的基本用法,如果是要將第三方庫注入的話,使用@Inject注解就不能解決問題了,此時,就用到了另一個注解@Module。
下面,讓我們試一試hilt今天Retrofit是如何聯動的吧
引入Retrofit
首先,需要引入Retrofit
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.google.code.gson:gson:2.8.6'
Retrofit Module
成功引入之后,需要添加一個文件進行Retrofit的初始化工作
@Module
@InstallIn(ApplicationComponent::class)
class Network {
@Provides
@Singleton
fun provideOkHttpClient(): OkHttpClient {
return OkHttpClient.Builder()
.build()
}
@Provides
@Singleton
fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit {
return Retrofit.Builder()
.client(okHttpClient)
.baseUrl("https://wanandroid.com")
.addConverterFactory(GsonConverterFactory.create())
.build()
}
@Provides
@Singleton
fun provideService(retrofit: Retrofit): ChapterApi {
return retrofit.create(ChapterApi::class.java)
}
}
- @InstallIn注解,意思是將這個模塊安裝到Application中去,這樣,聲明周期就和Application一致了
- @Provides 常用于被 @Module 注解標記類的內部的方法,并提供依賴項對象
- 因為每次加入注解,都會創建一個實例,而我們的想法自然是單例,提高效率,這也很簡單,加上@Singleton就可以了。
嘗試獲取數據
既然準備工作都好了,就開始試著獲取數據吧
創建Api
第一步自然是創建Retrofit獲取數據的api,以wanAndroid的后臺api為依托。
interface ChapterApi {
@GET("/wxarticle/chapters/json")
suspend fun getChapter()
}
創建Bean文件
新建Bean文件,借助于android studio插件kotlin data classes from Json自定生成數據類
MainActivity
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject lateinit var service: ChapterApi
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initChapter()
}
private fun initChapter() {
GlobalScope.launch {
val chapterData: ChapterData = service.getChapter()
Log.d(Util.TAG,chapterData.errorMsg + chapterData.errorCode)
}
}
}
- 這是主界面實現的代碼,這里只是簡單的獲取了errorCode和errorMsg,但是能看到,代碼已經非常非常簡潔了
- 借助于kotlin的協程,替代原本網絡請求中的回調,代碼行數減少了,效率自然就高了