Dependency Injection (DI) is a design pattern to decouple the conventional dependency relationship between objects. When it comes to DI in android Dagger always takes a lead. But it is very complex and requires a lot of boilerplate codes in order to set up the Dagger. So, to overcome this problem Hilt was introduced. Dagger Hilt simplifies the whole process and reduces the unnecessary steps without losing any of the features of the original Dagger.
Example
In this example, we will build an android application that will show the list of cryptocurrencies using the MVVM design pattern and for dependency injection, we will be using Dagger Hilt.
Step 1: Create a New Project
To create a new project in Android Studio please refer to How to Create/Start a New Project in Android Studio. Note that select Kotlin as the programming language.
Step 2: Adding dependencies
In order to use Dagger Hilt, we need to add the dependencies for it. First, we will add the classpath to our project-level build.gradle file. For adding this dependency Go to Gradle Scripts > build.gradle(Project:app) add the following dependency. After adding these dependencies you need to click on Sync Now.
dependencies {
classpath 'com.google.dagger:hilt-android-gradle-plugin:2.38.1'
}
Now, in the app-level build.gradle file we need to place a plugin. For this, Go to Gradle Scripts > build.gradle(Module:app) add the following plugin.
plugins {
id 'kotlin-android'
id 'kotlin-kapt'
id 'dagger.hilt.android.plugin'
}
We also need to add the dependency in the same app-level build.gradle file. Following dependency needs to be added inside the build.gradle(Module:app).
dependencies {
implementation 'com.github.bumptech.glide:glide:4.9.0'
implementation 'com.google.dagger:hilt-android:2.38.1'
kapt 'com.google.dagger:hilt-android-compiler:2.38.1'
kapt 'androidx.hilt:hilt-compiler:1.0.0'
implementation "androidx.activity:activity-ktx:1.4.0"
}
We also need to add property attributes in gradle.properties file. Go to Gradle Scripts > gradle.properties and add the following property.
kapt.use.worker.api=false
After adding plugins and dependencies you need to click on Sync Now. Before moving further let’s add some color attributes in order to enhance the app bar. Go to app > res > values > colors.xml and add the following color attributes.
XML
< resources >
< color name = "colorPrimary" >#0F9D58</ color >
< color name = "colorPrimaryDark" >#16E37F</ color >
< color name = "colorAccent" >#03DAC5</ color >
</ resources >
|
Step 3: Creating An HiltApplication Class
In this step, we will create an HiltApplication.kt class and annotate this class with @HiltAndroidApp annotations. This will make this class trigger Hilt’s code generation which will have the base class for our application and it acts as the application-level dependency container. For this go to app > java > package > right-click > New > Kotlin Class/File and name it as HiltApplication.
Kotlin
import android.app.Application
import dagger.hilt.android.HiltAndroidApp
@HiltAndroidApp
class HiltApplication :Application(){
}
|
Once we are done with this, we need to update our AndroidManifest.xml file by providing our HiltApplication.kt class name as android:name inside <application></application> tag. Also, we need to add Internet permission in the AndroidManifest file.
XML
< uses-permission android:name = "android.permission.INTERNET" />
< application
android:name = ".HiltApplication" >
</ application >
|
Step 4: Creating Data Class And A Repository
In this step, first, we will create a Data class and name it Cryptocurrency. This class will have two member variable images and a name. For this Go to app > java > package > Right-click > New > Kotlin Class/File.
Kotlin
data class Cryptocurrency(
val image:String,
val name:String
)
|
Now, we will create a Repository class that will have a few sample data which we will inject into our ViewModel. We will make this Repository abstract by making it an interface and providing an implementation to it. For creating this Go to app > java > package > Right-click > New > Kotlin Class/File and make its type as interface and give its name as CryptocurrencyRepository and add the following interface code to it.
Kotlin
interface CryptocurrencyRepository {
fun getCryptoCurrency(): List<Cryptocurrency>
}
|
Now, we will provide an implementation to this interface class. For this Go to app > java > package > Right-click > New > Kotlin Class/File and create a new class and name it as CryptocurrencyRepositoryImpl.kt, In this class we will override the getCryptoCurrency() method and provide an implementation to it. The cryptocurrency data class will have two string type attributes one for image and the other for the name.
Kotlin
class CryptocurrencyRepositoryImpl : CryptocurrencyRepository{
override fun getCryptoCurrency() = listOf(
)
}
|
Step 5: Creating A Module Class
In this step, we will create a module class and name it as AppModule. For this Go to app > java > package > Right-click > New > Kotlin Class/File and create a new class and name it as AppModule.kt. AppModule class will inject dependency to other classes so, we need to annotate this class with @Module annotation which will make this class a module to inject dependency to other classes within its scope. Furthermore, we will also add one more annotation to it. i.e., @InstallIn(SingletonComponent::class) this will make this class inject dependencies across the entire application.
Kotlin
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn (SingletonComponent:: class )
class AppModule {
@Provides
@Singleton
fun provideCryptocurrencyRepository():CryptocurrencyRepository=CryptocurrencyRepositoryImpl()
}
|
Here, we have also added the provision for CryptocurrencyRepository using @Provides annotation. Along with this, we have used @Singleton annotation so that whenever we inject the dependency we inject the same single instance of CryptocurrencyRepository were ever requested.
Step 6: Creating A ViewModel
In this step, we will create a ViewModel. In order to do so, Go to app > java > package > Right-click > New > Kotlin Class/File and create a new class and name it as MainViewModel.kt. We will annotate it with @HiltViewModel which makes the models to be created using Hilt’s model factory that makes it easier to be used with Activities and Fragments. We will also annotate exactly one constructor with @Inject annotation, using this constructor we will inject all the dependencies to our view model class. Below is the code for MainViewModel.kt class.
Kotlin
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
@HiltViewModel
class MainViewModel @Inject constructor(
private val cryptocurrencyRepository: CryptocurrencyRepository
) : ViewModel() {
private val cryptocurrencyEmitter = MutableLiveData<List<Cryptocurrency>>()
val cryptoCurrency: LiveData<List<Cryptocurrency>> = cryptocurrencyEmitter
init {
loadCryptocurrency()
}
private fun loadCryptocurrency() {
cryptocurrencyEmitter.value = cryptocurrencyRepository.getCryptoCurrency()
}
}
|
Step 7: Create An Adapter Class
In this step, we will create an adapter. In order to do so, Go to app > java > package > Right-click > New > Kotlin Class/File and create a new class and name it as CryptocurrencyAdapter.kt. This will provide access to our cryptocurrency data items and be responsible for making a View for each item in the cryptocurrency data list.
Kotlin
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
class CryptocurrencyAdapter( private val cryptocurrency: List<Cryptocurrency>) : RecyclerView.Adapter<CryptocurrencyAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.list_item, parent, false )
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(cryptocurrency[position])
}
override fun getItemCount() = cryptocurrency.size
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(index: Cryptocurrency) {
Glide.with(itemView.context)
.load(index.image).dontAnimate()
.into(itemView.findViewById(R.id.image))
itemView.findViewById<TextView>(R.id.cryptocurrency).text = index.name
}
}
}
|
Step 8: Create list_item.xml File
In this step, we will create a list_item.xml file which will contain the views for our image and the name of the cryptocurrency. For this Go to app > res > layout > Right-click > New > Layout Resource File and create a resource file and name it list_item.xml and add the following lines of codes to it.
XML
<? xml version = "1.0" encoding = "utf-8" ?>
< androidx.constraintlayout.widget.ConstraintLayout
android:layout_width = "match_parent"
android:layout_height = "wrap_content"
android:padding = "16dp"
android:layout_margin = "8dp"
android:background = "#f5f5f5"
< ImageView
android:id = "@+id/image"
android:layout_width = "64dp"
android:layout_height = "64dp"
android:scaleType = "fitXY"
android:layout_margin = "24dp"
android:importantForAccessibility = "no"
app:layout_constraintStart_toStartOf = "parent"
app:layout_constraintTop_toTopOf = "parent"
app:layout_constraintBottom_toBottomOf = "parent" />
< TextView
android:id = "@+id/cryptocurrency"
android:layout_width = "wrap_content"
android:layout_height = "wrap_content"
android:layout_margin = "24dp"
android:textSize = "24sp"
app:layout_constraintStart_toEndOf = "@id/image"
app:layout_constraintTop_toTopOf = "parent"
app:layout_constraintBottom_toBottomOf = "parent"
tools:text = "Cryptocurrencies" />
</ androidx.constraintlayout.widget.ConstraintLayout >
|
Step 9: Create activity_main.xml File
In this step, we will make the layout for our activity_main.xml file. We will just add a RecyclerView to our layout.
XML
<? xml version = "1.0" encoding = "utf-8" ?>
< androidx.constraintlayout.widget.ConstraintLayout
android:layout_width = "match_parent"
android:layout_height = "match_parent"
tools:context = ".MainActivity" >
< androidx.recyclerview.widget.RecyclerView
android:id = "@+id/cryptocurrency_list"
android:layout_width = "match_parent"
android:layout_height = "match_parent" />
</ androidx.constraintlayout.widget.ConstraintLayout >
|
Step 10: Working With MainActivity File
Finally, we work with our MainActivity. First, we annotate it with @AndroidEntryPoint annotation which will make the component class(activity, fragments, views, services, and broadcast receiver) ready for injection. Also, we will get our ViewModel in MainActivity by using by viewModles() delegate. After that, we are just observing our LiveData.
Kotlin
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private lateinit var cryptocurrencyList: RecyclerView
private val viewModel:MainViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super .onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
cryptocurrencyList = findViewById<RecyclerView>(R.id.cryptocurrency_list)
cryptocurrencyList.layoutManager = LinearLayoutManager( this )
observeCryptoCurrency()
}
private fun observeCryptoCurrency() {
viewModel.cryptoCurrency.observe( this ) {
cryptocurrencyList.adapter = CryptocurrencyAdapter(it)
}
}
}
|
Output:
Share your thoughts in the comments
Please Login to comment...