Slide 1

Slide 1 text

Android Dependency Injection with Hilt Thuy Phan

Slide 2

Slide 2 text

What is Dependency Injection?

Slide 3

Slide 3 text

class Car { private val engine = Engine() fun start() { engine.start() } } fun main(args: Array) { val car = Car() car.start() }

Slide 4

Slide 4 text

class Car(private val engine: Engine) { fun start() { engine.start() } } fun main(args: Array) { val engine = Engine() val car = Car(engine) car.start() }

Slide 5

Slide 5 text

Advantages of DI ● Reusability of classes and decoupling dependencies ● Ease of refactoring ● Ease of testing

Slide 6

Slide 6 text

Is DI hard in Android? ● Framework classes are instantiated by the SDK ● Factories added on API 28, not realistic

Slide 7

Slide 7 text

Dagger? ● Hard to configure ● Multiple paths to do the same thing

Slide 8

Slide 8 text

@Component @Subcomponent @Inject @Module Dagger Android Dagger AndroidInjector @ContributesAndroidInjector HasAndroidInjector DispatchingAndroidInjector

Slide 9

Slide 9 text

Wait! I just want to code my feature???!!!

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

DI Solution Golds ● Opinionated ● Easy to setup ● Let’s us focus on what is important

Slide 12

Slide 12 text

Hilt

Slide 13

Slide 13 text

Hilt ● Standardize DI in Android ● Build on the top of Dagger ● Tooling support ● AndroidX Extensions

Slide 14

Slide 14 text

Setup

Slide 15

Slide 15 text

1. Hilt application class

Slide 16

Slide 16 text

@HiltAndroidApp class ExampleApplication : Application() { ... }

Slide 17

Slide 17 text

1. Hilt application class 2. Inject dependencies into Android classes

Slide 18

Slide 18 text

@AndroidEntryPoint class ExampleActivity : AppCompatActivity() { @Inject lateinit var analytics: AnalyticsAdapter ... }

Slide 19

Slide 19 text

1. Hilt application class 2. Inject dependencies into Android classes 3. Define Hilt bindings

Slide 20

Slide 20 text

class AnalyticsAdapter @Inject constructor( private val service: AnalyticsService ) { ... }

Slide 21

Slide 21 text

1. Hilt application class 2. Inject dependencies into Android classes 3. Define Hilt bindings 4. Hilt modules

Slide 22

Slide 22 text

interface AnalyticsService { fun analyticsMethods() } // Constructor-injected, because Hilt needs to know how to // provide instances of AnalyticsServiceImpl, too. class AnalyticsServiceImpl @Inject constructor( ... ) : AnalyticsService { ... } @Module @InstallIn(ActivityComponent::class) abstract class AnalyticsModule { @Binds abstract fun bindAnalyticsService( analyticsServiceImpl: AnalyticsServiceImpl ): AnalyticsService }

Slide 23

Slide 23 text

@Module @InstallIn(ActivityComponent::class) object AnalyticsModule { @Provides fun provideAnalyticsService ( // Potential dependencies of this type ): AnalyticsService { return Retrofit.Builder() .baseUrl( "https://example.com" ) .build() .create(AnalyticsService:: class.java) } }

Slide 24

Slide 24 text

Provide multiple bindings for the same type

Slide 25

Slide 25 text

Predefined qualifiers in Hilt @ApplicationContext @ActivityContext

Slide 26

Slide 26 text

No content

Slide 27

Slide 27 text

@EntryPoint

Slide 28

Slide 28 text

No content

Slide 29

Slide 29 text

Hilt Annotations @HiltAndroidApp @AndroidEntryEnpoint @InstallIn @EntryPoint

Slide 30

Slide 30 text

Hilt and Jetpack integrations

Slide 31

Slide 31 text

class ExampleViewModel @ViewModelInject constructor( private val repository: ExampleRepository, @Assisted private val savedStateHandle: SavedStateHandle ) : ViewModel() { ... } @AndroidEntryPoint class ExampleActivity : AppCompatActivity() { private val exampleViewModel: ExampleViewModel by viewModels() ... }

Slide 32

Slide 32 text

class ExampleWorker @WorkerInject constructor( @Assisted appContext: Context, @Assisted workerParams: WorkerParameters, workerDependency: WorkerDependency ) : Worker(appContext, workerParams) { ... } @HiltAndroidApp class ExampleApplication : Application(), Configuration.Provider { @Inject lateinit var workerFactory: HiltWorkerFactory override fun getWorkManagerConfiguration() = Configuration.Builder() .setWorkerFactory(workerFactory) .build() }

Slide 33

Slide 33 text

Testing

Slide 34

Slide 34 text

@HiltAndroidTest class SettingsActivityTest { @get:Rule var hiltRule = HiltAndroidRule(this) @Inject lateinit var analyticsAdapter: AnalyticsAdapter @Before fun init() { hiltRule.inject() } @Test fun `happy path`() { ... } }

Slide 35

Slide 35 text

@UninstallModules(AnalyticsModule ::class) @HiltAndroidTest class SettingsActivityTest { @Module @InstallIn(ApplicationComponent ::class) abstract class TestModule { @Singleton @Binds abstract fun bindAnalyticsService ( fakeAnalyticsService : FakeAnalyticsService ): AnalyticsService } @BindValue @JvmField val analyticsService : AnalyticsService = FakeAnalyticsService () }

Slide 36

Slide 36 text

@UninstallModules(AnalyticsModule ::class) @HiltAndroidTest class SettingsActivityTest { @BindValue @JvmField val analyticsService : AnalyticsService = FakeAnalyticsService () }

Slide 37

Slide 37 text

@CustomTestApplication(BaseApplication::class) interface HiltTestApplication

Slide 38

Slide 38 text

class CustomTestRunner : AndroidJUnitRunner() { override fun newApplication(cl: ClassLoader?, name: String?, context: Context?): Application { return super.newApplication(cl, HiltTestApplication::class.java.name, context) } } ——————————————————————————————————————————————— android { defaultConfig { testInstrumentationRunner "com.example.android.dagger.CustomTestRunner" } }

Slide 39

Slide 39 text

Hilt Testing Annotations @HiltAndroidTest @UninstallModules @BindValue @CustomTestApplication

Slide 40

Slide 40 text

Alpha Annotation processors and code generation could make build slower Android Official Build on Dagger but easy to use IDE support Android lifecycle aware Build Type award Coexist with Dagger Cheatsheet - Good docs - Codelabs Pros Cos

Slide 41

Slide 41 text

Learn more Dependency injection with Hilt Dagger & Hilt documentation Dagger & Hilt repository Migrating your Dagger app to Hilt

Slide 42

Slide 42 text

Thank you!