
Dependency Injection (DI) is a technique in software development where one object provides the dependencies needed by another object. Instead of creating these dependencies internally, they are supplied externally, making the code easier to manage, test, and expand. This approach is especially useful in Android development to build modular and scalable apps.
What is Dependency Injection?
Think of DI as a way to “inject” the things a class needs (its dependencies) without that class having to create them itself. This simplifies the code and makes it easier to test and maintain.
Key Concepts of Dependency Injection
- Dependencies: The resources or objects a class needs to work (e.g., a database or API service).
- Injection: The process of providing these dependencies.
Why Use Dependency Injection?
- Better Testing: Allows you to test with mock objects instead of real ones.
- Loose Coupling: Classes donβt depend directly on each other, making them more flexible.
- Easier Maintenance: You can change dependencies without affecting other code.
- Scalability: Adding new features becomes simpler.
How to Implement Dependency Injection in Android
Here are the main ways DI can be applied:
1. Constructor Injection
Dependencies are passed through the class constructor.
class Engine
class Car(private val engine: Engine)
When testing:
val mockEngine = MockEngine()
val car = Car(mockEngine)
2. Field Injection
Dependencies are directly injected into the class fields.
class Car {
@Inject lateinit var engine: Engine
}
3. Method Injection
Dependencies are provided through setter methods.
class Car {
lateinit var engine: Engine
fun setEngine(engine: Engine) {
this.engine = engine
}
}
Popular DI Frameworks for Android
1. Hilt
A library built on Dagger, designed specifically for Android.
- Key Features:
- Simple to use with annotations like
@Inject
and@HiltAndroidApp
. - Handles Android lifecycle-aware dependencies.
- Easy integration with Android components like Activities and ViewModels.
- Simple to use with annotations like
Example:
@HiltAndroidApp
class MyApp : Application()
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject lateinit var repository: MyRepository
}
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Provides fun provideRepository(): MyRepository = MyRepository()
}
2. Dagger 2
A widely-used DI framework that works during compile time for better performance.
- Key Features:
- Generates dependency graphs during build time.
- Flexible and allows for detailed customizations.
- Works well for large-scale projects.
Example:
@Module
class AppModule {
@Provides
fun provideSharedPreferences(context: Context): SharedPreferences {
return context.getSharedPreferences("MyPrefs", Context.MODE_PRIVATE)
}
}
@Component(modules = [AppModule::class])
interface AppComponent {
fun inject(activity: MainActivity)
}
3. Koin
A lightweight DI framework written in Kotlin, focusing on simplicity and runtime injection.
- Key Features:
- No annotations, just Kotlin code.
- Easy to set up with minimal boilerplate.
Example:
val appModule = module {
single { Repository() }
}
startKoin {
modules(appModule)
}
class MainActivity : AppCompatActivity() {
val repository: Repository by inject()
}
4. Manual Dependency Injection
For small projects, you can manage dependencies manually without a library.
- Key Features:
- Full control over dependencies.
- Simple for small apps but gets tedious for larger projects.
Example:
class Repository(val dataSource: DataSource)
class MainActivity {
private val repository = Repository(DataSource())
}
Choosing the Right DI Approach
- Hilt: Best for most Android apps; easy to use and integrates well with Android components.
- Dagger 2: Ideal for large, complex apps that need fine-grained control.
- Koin: Great for Kotlin-focused projects and smaller apps.
- Manual DI: Works well for simple apps or when avoiding libraries.
How DI Benefits Real Apps
For example, in an e-commerce app, you may need dependencies like:
SearchService
for product searches.OrderService
for managing orders.NotificationService
for sending updates.
Using DI, you can define these services in a central place and inject them wherever needed, reducing redundancy and improving maintainability.
Conclusion
Dependency Injection is a fundamental principle for creating clean, testable, and scalable Android applications. Whether you choose Hilt, Dagger 2, Koin, or manual DI, adopting this pattern simplifies development and ensures better code quality.