# Dependency Injection by Hilt ## Hilt 是基於Dagger開發的,根據官方介紹有以下優點 1. 程式碼可重複使用 2. 易於重構 3. 易於測試 #### 程式碼架構 [參考這篇](https://itnext.io/android-architecture-hilt-mvvm-kotlin-coroutines-live-data-room-and-retrofit-ft-8b746cab4a06) ,他的code有蠻多不錯的寫法,運用了蠻多的 '*泛型* ' 與'*高階函數* ' #### 不免俗的還是上一張MVVM架構圖   #### 進入主題 hilt 1. gradle 引用 (注意kapt 跟hilt core版本要一樣,有些狀況編譯會錯誤) ```kotlin= // project dependencies { ext.hilt_version = '2.35' //hilt classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version" } // app plugins { // hilt support id 'kotlin-kapt' id 'dagger.hilt.android.plugin' } dependencies { //hilt support implementation 'com.google.dagger:hilt-android:2.35' implementation "androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha03" kapt 'com.google.dagger:hilt-android-compiler:2.35' kapt "androidx.hilt:hilt-compiler:1.0.0" implementation "androidx.hilt:hilt-navigation-fragment:1.0.0" } ``` 2. 一切源頭從@HiltAndroidApp開始 **於專案目錄下新增application class,並將路徑加入manifest** ```kotlin= @HiltAndroidApp class MainApplication : Application(){ override fun onCreate() { super.onCreate() if (BuildConfig.DEBUG) { Timber.plant(Timber.DebugTree()) } } } //於manifest加入剛剛新增的application讓app啟動時載入 <application android:name=".MainApplication" ``` 3. 在需要注入的類別加上聲明 @AndroidEntryPoint 原作者在MainActivity也有聲明,因為Fragment是依附在Activity底下。 ```kotlin= @AndroidEntryPoint class CharactersFragment : Fragment(), CharactersAdapter.CharacterItemListener { } @AndroidEntryPoint class CharacterDetailFragment: Fragment() { } @AndroidEntryPoint class MainActivity : AppCompatActivity() { } ``` 4. 注入,在類別建構子使用 @Inject constructor()或者在變數前@Inject var myVar = xxxx 這個例子,發出網路請求的起點應該是從viewModel這裡開始的。在這裏聲明@hiltviewModel功能跟@AndroidEntryPoint一樣, 那@Inject constructor()是一個提供instance的概念,hilt會自動產生CharactersViewModel的instance, ```kotlin= @HiltViewModel class CharactersViewModel @Inject constructor( repository: CharacterRepository ) : ViewModel() { val characters = repository.getCharacters() } ``` 那我們繼續往下追,CharactersViewModel要產生instance需要一個參數,repository: CharacterRepository,這個參數他是一個類別 ```kotlin= class CharacterRepository @Inject constructor( private val remoteDataSource: CharacterRemoteDataSource, private val localDataSource: CharacterDao ) ``` 在繼續往下追,CharacterRepository要產生instance需要兩參數: remoteDataSource: CharacterRemoteDataSource, localDataSource: CharacterDao 我們先看第一個參數CharacterRemoteDataSource,然而要產生instance需要變數CharacterService ```kotlin= class CharacterRemoteDataSource @Inject constructor(private val characterService: CharacterService) : BaseDataSource() ``` 追到CharacterService發現他是一個interface,他無法利用建構子產生instance,到這就走不下去了,稍後提到的Module會解決此問題 ```kotlin= interface CharacterService { @GET("character") suspend fun getAllCharacters(): Response<CharacterList> @GET("character/{id}") suspend fun getCharacter(@Path("id") id: Int): Response<Character> } ``` 相同地,第二個變數CharacterDao他也是一個interface 接下來我們來談談Module吧 ================== 5. 那些無法使用@Inject提供instance的類別或者介面,hilt要去哪兒找勒? @Module 登場 AppModule這個object,來瞧瞧他寫了些什麼 --- * 我們先看CharacterService,他是一個介面,沒有建構子,在Module裏面,provideCharacterService的instance是透過Retrofit間接取得 * 而Retrofit也是無法透過建構子產生instance,一樣要在Module定義 * Gson一樣道理 ```kotlin= @Provides fun provideCharacterService(retrofit: Retrofit): CharacterService = retrofit.create(CharacterService::class.java) @Singleton @Provides fun provideRetrofit(gson: Gson): Retrofit = Retrofit.Builder() .baseUrl("https://rickandmortyapi.com/api/") .addConverterFactory(GsonConverterFactory.create(gson)) .build() @Provides fun provideGson(): Gson = GsonBuilder().create() ``` * 接下來看第二個變數CharacterDao,他也是一個interface,沒有建構子可以產生instance * 他是透過資料庫間接取得 * 在定義取得資料庫的instance ```kotlin= @Singleton @Provides fun provideDatabase(@ApplicationContext appContext: Context) = AppDatabase.getDatabase(appContext) @Singleton @Provides fun provideCharacterDao(db: AppDatabase) = db.characterDao() ``` --- ##### 所以Module主要是在定義無法使用@Inject constructor()取得instance的函數,提供給hilt注入。 ##### 經過這樣分析下來,從viewModel發出網路請求,一路上所需要的instance要從哪裡Inject與Module定義就都完成了,剩下的就是完成商業邏輯了 ##### 以上只是Hilt最基本的應用 ###### tags: `hilt` `DI` `kotlin` `Android`
×
Sign in
Email
Password
Forgot password
or
Sign in via Google
Sign in via Facebook
Sign in via X(Twitter)
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
Continue with a different method
New to HackMD?
Sign up
By signing in, you agree to our
terms of service
.