In this article let's explore how easy to implement the Hilt DI and annotate the Network module to invoke the Countries API.
Components:
Components used in the Project.
- Hilt
- Coroutine
- Retrofit
- Gson
- ViewBinding
- Timber
- ConstraintLayout
- RecyclerView
API
Rest Countries -Get information about countries via a RESTful API.
Method | URL |
---|---|
GET | https://restcountries.eu/rest/v2/region/europe |
Project Structure
Package Structure
Implementation
Follow the steps to implement the Hilt DI.
Add Dependencies
Project Level
- Add the
hilt-android-gradle-plugin
into the classpath of the project levelbuild.gradle
.
buildscript{ext.kotlin_version="1.5.20-M1"ext.gradle_version="4.2.1"ext.hilt_version='2.35'ext.core_version='1.5.0'ext.appcompat_version='1.3.0'ext.material_version='1.3.0'ext.constraint_version='2.0.4'ext.coroutines_version='1.5.0'ext.lifecycle_version='2.3.1'ext.activity_version='1.2.3'ext.retrofit_version='2.6.0'ext.httplogging_version='3.12.0'ext.json_version='2.8.6'ext.junit_version='4.13.2'ext.extjunit_version='1.1.2'ext.espressocore_version='3.3.0'ext.timber_version='4.7.1'dependencies{classpath"com.google.dagger:hilt-android-gradle-plugin:$hilt_version"}}
App Level
- Add the
dagger.hilt.android.plugin
plugin into the app levelbuild.gradle
. - Enable the support for Java 8 features by adding
compileOptions
in thebuild.gradle
. - Add the
dagger:hilt-android
implementation anddagger:hilt-compiler
kapt into thebuild.gradle
.
plugins{id'com.android.application'id'kotlin-android'id'kotlin-kapt'id'kotlin-android-extensions'id'dagger.hilt.android.plugin'}android{compileOptions{sourceCompatibilityJavaVersion.VERSION_1_8targetCompatibilityJavaVersion.VERSION_1_8}kotlinOptions{jvmTarget='1.8'}buildFeatures{viewBindingtrue}}dependencies{// Kotlinimplementation"org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"// Coreimplementation"androidx.core:core-ktx:$core_version"implementation"androidx.appcompat:appcompat:$appcompat_version"implementation"androidx.activity:activity-ktx:$activity_version"// UIimplementation"com.google.android.material:material:$material_version"implementation"androidx.constraintlayout:constraintlayout:$constraint_version"// Lifecycle Componentsimplementation"androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"implementation"androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"// Loggingimplementation"com.jakewharton.timber:timber:$timber_version"// Hilt DIimplementation"com.google.dagger:hilt-android:$hilt_version"kapt"com.google.dagger:hilt-compiler:$hilt_version"// Coroutinesimplementation"org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"implementation"org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"//Retrofitimplementation"com.squareup.retrofit2:retrofit:$retrofit_version"implementation"com.squareup.retrofit2:converter-gson:$retrofit_version"implementation"com.squareup.okhttp3:logging-interceptor:$httplogging_version"implementation"com.google.code.gson:gson:$json_version"// TesttestImplementation"junit:junit:$junit_version"androidTestImplementation"androidx.test.ext:junit:$extjunit_version"androidTestImplementation"androidx.test.espresso:espresso-core:$espressocore_version"}
Manifest
- Internet permission(
uses-permission
) is required for accessing API. - Application class should be referred in the
android:name
attribute.
<uses-permissionandroid:name="android.permission.INTERNET"/><applicationandroid:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:name=".core.HiltApplication"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/Theme.MyHiltApplication"><activityandroid:name=".main.view.MainActivity"><intent-filter><actionandroid:name="android.intent.action.MAIN"/><categoryandroid:name="android.intent.category.LAUNCHER"/></intent-filter></activity></application>
Application
Create Application class and annotate with @HiltAndroidApp.
@HiltAndroidApp
annotation generates Hilt code for Application class and creates parent container.
@HiltAndroidAppclassHiltApplication:Application()
Activity
Create an Activity and setup the UI & livedata observers. Here, viewbinding is used for inflating the UI. And viewmodel is initialized using theby viewModels()
ktx.
@AndroidEntryPoint
annotation generates Hilt components for Android classes like Activity, Fragment etc., based on the lifecycle of the respective component.
@AndroidEntryPointclassMainActivity:AppCompatActivity(){privatelateinitvarbinding:ActivityMainBindingprivatevalmainViewModel:MainViewModelbyviewModels()@InjectlateinitvarcountryAdapter:CountryAdapteroverridefunonCreate(savedInstanceState:Bundle?){super.onCreate(savedInstanceState)binding=ActivityMainBinding.inflate(layoutInflater)setContentView(binding.root)setUpUI()setUpObservers()}}
Adapter
Create the Adapter class for recyclerview with constructor injection.
@Inject
annotation used in constructor, field and method injection where dependency is requested. Field injected cannot be private.
classCountryAdapter@Injectconstructor():RecyclerView.Adapter<CountryAdapter.ViewHolder>(){varcountries:List<Country>=emptyList()overridefunonCreateViewHolder(parent:ViewGroup,viewType:Int):ViewHolder=ViewHolder(CountryItemBinding.inflate(LayoutInflater.from(parent.context),parent,false))overridefunonBindViewHolder(holder:ViewHolder,position:Int)=holder.bind(countries[position])overridefungetItemCount():Int=countries.sizeinnerclassViewHolder(privatevalbinding:CountryItemBinding):RecyclerView.ViewHolder(binding.root){funbind(country:Country){binding.apply{country.also{(name,capital)->nameTextview.text=namecapitalTextview.text=capital}}}}}
ViewModel
Create ViewModel class and add method for loading the country list.
@HiltViewModel
is annotated to enable viewmodel injection.
@HiltViewModelclassMainViewModel@Injectconstructor(privatevalrepository:Repository):ViewModel(){privatevalcountryLiveData=MutableLiveData<List<Country>?>()fungetCountry()=countryLiveDatainit{loadCountries()}}
Module
Create object class with various annotations like @Module, @InstallIn,@singleton and @Provides which provides dependencies. The Module class supplies the necessary dependent methods for the Network module.
@Module
annotated class provides required instances as dependency for various classes.Hilt Module annotated with
@InstallIn
specify the scope of the Module. Here,SingletonComponent::class
generates singleton container for the class.
@Provides
is annotated in the method and provides objects to inject when required.
@Singleton
is annotated to create singleton instance of the dependency object and use it throughout the app.
@Module@InstallIn(SingletonComponent::class)objectApiModule{privateconstvalBASE_URL="https://restcountries.eu/rest/v2/"@Singleton@ProvidesfunprovidesHttpLoggingInterceptor()=HttpLoggingInterceptor().apply{level=HttpLoggingInterceptor.Level.BODY}@Singleton@ProvidesfunprovidesOkHttpClient(httpLoggingInterceptor:HttpLoggingInterceptor):OkHttpClient=OkHttpClient.Builder().addInterceptor(httpLoggingInterceptor).build()@Singleton@ProvidesfunprovideRetrofit(okHttpClient:OkHttpClient):Retrofit=Retrofit.Builder().addConverterFactory(GsonConverterFactory.create()).baseUrl(BASE_URL).client(okHttpClient).build()@Singleton@ProvidesfunprovideApiService(retrofit:Retrofit):ApiService=retrofit.create(ApiService::class.java)@Singleton@ProvidesfunprovidesRepository(apiService:ApiService)=Repository(apiService)}
Service
Define the Countries list API's in the Service.
interfaceApiService{@GET("region/europe")suspendfungetCountries():Response<Countries>}
Repository
Create an Repository class which returns the list of countries network response.
classRepository(privatevalapiService:ApiService){suspendfungetCountries()=apiService.getCountries()}
Screenshot
Code
Project code is accessible in the GitHub RepoDevArena/HiltRetrofitApp
Happy Coding! 😀
Top comments(1)
For further actions, you may consider blocking this person and/orreporting abuse