tezvyn:

Dependency Injection: Your Objects Shouldn't Create Their Own Helpers

AI-drafted, machine-checkedSource: developer.android.combeginner
Dependency Injection: Your Objects Shouldn't Create Their Own Helpers

Instead of creating its own helpers (like a network client), an object receives them from an outside source. This is crucial for building testable Android apps, as it lets you swap a real `UserRepository` with a fake one during tests.

WHY IT EXISTS: To decouple components. When an object creates its own dependencies, like val db = DatabaseHelper(context), it's tightly coupled to that specific implementation. This makes testing difficult because you can't easily replace the real database with a fake one. Swapping implementations becomes a major refactoring task. DI solves this by externalizing dependency creation.

THE MENTAL MODEL: Think of a chef in a kitchen. A junior chef might run to the pantry to grab every ingredient they need (new Flour(), new Sugar()). A senior chef has a mise en place—all ingredients are prepped and provided to them before they start cooking. The chef doesn't create the ingredients; they are "injected" into their workspace. Your objects should be like the senior chef.

HOW IT WORKS: There are three main types of injection. First, Constructor Injection: dependencies are provided through the class's constructor. This is the most common and recommended approach. Second, Field Injection: dependencies are passed through public properties after the object is created, often used in Android frameworks like Activities where you don't control the constructor. Third, Method Injection: a dependency is passed into a specific method that needs it, not the whole object.

WHEN TO USE IT: Use DI for any non-trivial dependency your class needs to function. This includes network clients, database access objects (DAOs), shared preferences managers, and analytics trackers. It is the foundation of modern, scalable Android architecture.

WHEN NOT TO USE IT: Avoid DI for simple, self-contained value objects or data classes that have no external dependencies (like a User data class with just strings and integers). Overusing it for trivial, local objects can add unnecessary complexity.

ONE CANONICAL EXAMPLE: A LoginViewModel needs a UserRepository to handle user data. Without DI, it would create its own: class LoginViewModel { private val repository = UserRepository() }. This is hard to test. With DI, it requests it in the constructor: class LoginViewModel(private val repository: UserRepository) { ... }. Now, in a unit test, you can easily pass a FakeUserRepository instead of the real one.

Read the original → developer.android.com

Get five bites like this every day.

Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.