$30 off During Our Annual Pro Sale. View Details »

Hilt the DI ground running

Hilt the DI ground running

This talk was about Dependency Injection with Hilt at DevFest Egypt. First I explained the concept of Dependency Injection without libraries. Next, I showed how to incorporate Hilt into a new app and how to migrate an existing app.

Moyinoluwa Adeyemi

October 17, 2020
Tweet

More Decks by Moyinoluwa Adeyemi

Other Decks in Programming

Transcript

  1. Hilt the DI ground
    running
    Moyinoluwa Adeyemi
    @moyheen

    View Slide

  2. What is Dependency Injection?

    View Slide

  3. What is Dependency Injection?
    “dependency injection is a technique in which
    an object receives other objects that it depends on”
    According to good ol’ wikipedia

    View Slide

  4. Dependency Injection

    View Slide

  5. Dependency Injection

    View Slide

  6. No Dependency Injection
    class RoboGardener {
    fun waterPlant() {
    val waterSource = WaterSource()
    waterSource.dispenseWater()
    }
    }

    View Slide

  7. Dependency Injection - Constructor
    class RoboGardener(private val waterSource: WaterSource) {
    fun waterPlant() {
    waterSource.dispenseWater()
    }
    }

    View Slide

  8. Dependency Injection - Constructor
    class RoboGardener(private val waterSource: WaterSource) {
    fun waterPlant() {
    waterSource.dispenseWater()
    }
    }
    fun main() {
    val roboGardener = RoboGardener(CupOfWater())
    roboGardener.waterPlant()
    }

    View Slide

  9. Dependency Injection - Setter
    class RoboGardener {
    lateinit var waterSource: WaterSource
    fun waterPlant() {
    waterSource.dispenseWater()
    }
    }

    View Slide

  10. Dependency Injection - Setter
    class RoboGardener {
    lateinit var waterSource: WaterSource
    fun waterPlant() {
    waterSource.dispenseWater()
    }
    }
    fun main() {
    val roboGardener = RoboGardener()
    roboGardener.waterSource = WateringCan()
    roboGardener.waterPlant()
    }

    View Slide

  11. Dependency Injection - Interface
    interface WaterSourceManager {
    fun provideWaterSource(waterSource: WaterSource)
    }

    View Slide

  12. Dependency Injection - Interface
    interface WaterSourceManager {
    fun provideWaterSource(waterSource: WaterSource)
    }
    class RoboGardener() : WaterSourceManager {
    private lateinit var waterSource: WaterSource
    override fun provideWaterSource(waterSource: WaterSource) {
    this.waterSource = waterSource
    }
    ...

    View Slide

  13. Dependency Injection - Interface
    interface WaterSourceManager {
    fun provideWaterSource(waterSource: WaterSource)
    }
    class RoboGardener() : WaterSourceManager {
    private lateinit var waterSource: WaterSource
    override fun provideWaterSource(waterSource: WaterSource) {
    this.waterSource = waterSource
    }
    fun waterPlant() {
    waterSource.dispenseWater()
    }
    }
    fun main() {
    val roboGardener = RoboGardener()
    roboGardener.provideWaterSource(Hose())
    roboGardener.waterPlant()
    }

    View Slide

  14. RoboGardener WaterSource
    Dependency Injection - Graph

    View Slide

  15. Service Locators

    View Slide

  16. Service Locators
    class RoboGardener {
    fun waterPlant() {
    val waterSource = CupOfWater()
    waterSource.dispenseWater()
    }
    }

    View Slide

  17. class RoboGardener {
    fun waterPlant() {
    val waterSource = ServiceLocator.getInstance.findWaterSource()
    waterSource.dispenseWater()
    }
    }
    Service Locators

    View Slide

  18. No
    increase
    in app
    size
    Manual Dependency Injection

    View Slide

  19. Manual Dependency Injection
    No
    increase
    in app
    size

    Low
    Learning
    Curve

    View Slide

  20. High
    maintenance
    costs
    Manual Dependency Injection
    Low
    Learning
    Curve
    No
    increase
    in app
    size



    View Slide

  21. Dependency Injection - Dagger
    Dagger
    Code generation
    Steep learning curve
    For Java or Kotlin developers
    Useful for large projects

    View Slide

  22. Dependency Injection - Hilt
    What is different?

    View Slide

  23. Dependency Injection - Hilt
    Low
    Learning
    Curve

    View Slide

  24. Dependency Injection - Hilt
    Low
    Learning
    Curve

    Free
    Lifecycle
    Management

    View Slide

  25. Dependency Injection - Hilt
    Low
    Learning
    Curve

    Free
    Lifecycle
    Management

    Co-exist
    with
    Dagger

    View Slide

  26. Dependency Injection - Hilt
    Usage - Annotations

    View Slide

  27. @HiltAndroidApp
    class HiltSampleApplication : Application()
    @HiltAndroidApp

    View Slide

  28. @AndroidEntryPoint
    class MainActivity : AppCompatActivity() {
    @Inject lateinit var imageLoader: ImageLoader
    private val viewModel: HiltSampleViewModel by viewModels()
    ...
    @AndroidEntryPoint

    View Slide

  29. Activity
    imageLoader
    viewModel
    @AndroidEntryPoint

    View Slide

  30. @Module
    @InstallIn(ApplicationComponent::class)
    abstract class AppModule
    @InstallIn

    View Slide

  31. @Module
    @InstallIn(ApplicationComponent::class)
    abstract class AppModule {
    @Binds
    abstract fun bindImageLoader(imageLoader: GlideImageLoader): ImageLoader
    ...
    }
    @InstallIn

    View Slide

  32. @Module
    @InstallIn(ApplicationComponent::class)
    object NetworkModule {
    @Provides
    fun providesMoshi(): Moshi = Moshi.Builder()
    .build()
    ...
    @InstallIn

    View Slide

  33. class HiltSampleViewModel @ViewModelInject constructor(
    private val hiltSampleRepository: HiltSampleRepository
    ) : ViewModel() {
    ...
    ViewModels - @ViewModelInject

    View Slide

  34. ViewModel
    repository
    ViewModels - @ViewModelInject
    Activity
    imageLoader
    viewModel

    View Slide

  35. @AndroidEntryPoint
    @AndroidEntryPoint
    class MainActivity : AppCompatActivity() {
    @Inject lateinit var imageLoader: ImageLoader
    private val viewModel: HiltSampleViewModel by viewModels()
    ...

    View Slide

  36. @EntryPoint - Interface
    @EntryPoint
    @InstallIn(ApplicationComponent::class)
    interface ImageFeatureModule {
    fun imageLoader(): ImageLoader
    }

    View Slide

  37. @EntryPoint - Dependency retrieval
    val imageLoader = EntryPoints
    .get(applicationContext, ImageFeatureModule::class.java)
    .imageLoader()

    View Slide

  38. @EntryPoint - Interface retrieval
    EntryPointAccessors.fromApplication(
    applicationContext,
    ImageFeatureModule::class.java
    )

    View Slide

  39. Dependency Injection - Hilt
    Usage - Scoping

    View Slide

  40. https://developer.android.com/training/dependency-injection/hilt-android#component-scopes
    Scoping

    View Slide

  41. https://dagger.dev/hilt/component-hierarchy.svg
    Scoping - Hierarchy

    View Slide

  42. Scoping - Unscoped bindings
    @Module
    @InstallIn(ApplicationComponent::class)
    abstract class AppModule {
    @Binds
    abstract fun bindImageLoader(imageLoader: GlideImageLoader): ImageLoader
    ...

    View Slide

  43. Scoping - Scoped bindings
    @Module
    @InstallIn(ApplicationComponent::class)
    abstract class AppModule {
    @Singleton
    @Binds
    abstract fun bindImageLoader(imageLoader: GlideImageLoader): ImageLoader
    ...

    View Slide

  44. Scoping - Scoped bindings
    @Module
    @InstallIn(ApplicationComponent::class)
    abstract class AppModule {
    @ActivityRetainedScoped
    @Binds
    abstract fun bindImageLoader(imageLoader: GlideImageLoader): ImageLoader
    ...

    View Slide

  45. Scoping - Scoped bindings

    View Slide

  46. class HiltSampleViewModel @ViewModelInject constructor(
    private val hiltSampleRepository: HiltSampleRepository
    ) : ViewModel() {
    ...
    ActivityRetainedComponent
    Scoping - ViewModel bindings

    View Slide

  47. Scoping - Predefined qualifiers
    @ActivityContext - Activity Context binding
    @ApplicationContext - Application Context binding

    View Slide

  48. Scoping - Predefined qualifiers
    class HiltSampleRepositoryImpl @Inject constructor(
    @ActivityContext private val context: Context,
    private val hiltSampleService: HiltSampleService
    ) : HiltSampleRepository {

    View Slide

  49. Scoping - Recommendation
    “only scope the binding if it’s required for the correctness of the
    code”

    View Slide

  50. Dependency Injection - Hilt
    Usage - Multi-module apps

    View Slide

  51. Multi-module apps - Gradle modules
    Regular gradle modules? ✅

    View Slide

  52. Multi-module apps - Feature modules
    Feature Modules?

    View Slide

  53. Multi-module apps - Feature modules
    “Hilt cannot process annotations in feature modules.
    You must use Dagger to perform dependency injection
    in your feature modules”
    Hilt + Dagger

    View Slide

  54. Multi-module apps - Feature modules
    1. Install an Entry Point in the Application Component from the app module

    View Slide

  55. Multi-module apps - Feature modules
    1. Install an Entry Point in the Application Component from the app module
    @EntryPoint
    @InstallIn(ApplicationComponent::class)
    interface ImageFeatureModule {
    fun imageLoader(): ImageLoader
    }

    View Slide

  56. Multi-module apps - Feature modules
    2. In the feature module, create a regular dagger component

    View Slide

  57. Multi-module apps - Feature modules
    2. In the feature module, create a regular dagger component
    @Component(dependencies = [ImageFeatureModule::class])
    interface ImageFeatureComponent {
    fun inject(activity: ImageFeatureModuleActivity)
    @Component.Builder
    interface Builder {
    fun dependencies(imageFeatureModule: ImageFeatureModule): Builder
    fun build(): ImageFeatureComponent
    }
    }

    View Slide

  58. Multi-module apps - Feature modules
    3. Inject the dependency in the feature module

    View Slide

  59. Multi-module apps - Feature modules
    3. Inject the dependency in the feature module
    class ImageFeatureModuleActivity : AppCompatActivity() {
    @Inject lateinit var imageLoader: ImageLoader
    @AndroidEntryPoint

    View Slide

  60. Multi-module apps - Feature modules
    3. Inject the dependency in the feature module
    ...
    override fun onCreate(savedInstanceState: Bundle?) {
    DaggerImageFeatureComponent.builder()
    .dependencies(
    EntryPointAccessors.fromApplication(
    applicationContext,
    ImageFeatureModule::class.java
    )
    )
    .build()
    .inject(this)

    View Slide

  61. Multi-module apps - Feature modules
    3. Inject the dependency in the feature module
    ...
    override fun onCreate(savedInstanceState: Bundle?) {
    DaggerImageFeatureComponent.builder()
    .dependencies(
    EntryPointAccessors.fromApplication(
    applicationContext,
    ImageFeatureModule::class.java
    )
    )
    .build()
    .inject(this)

    View Slide

  62. Dependency Injection - Migration
    Dagger Android -> Hilt

    View Slide

  63. Migration - build.gradle

    View Slide

  64. Migration - app/build.gradle

    View Slide

  65. Migration - app/build.gradle

    View Slide

  66. Migration - Application

    View Slide

  67. Migration - Hilt_HiltSampleApplication.java (generated)

    View Slide

  68. Migration - ViewModel

    View Slide

  69. Migration - HiltSampleViewModel_AssistedFactory.java (generated)

    View Slide

  70. Migration - HiltSampleViewModel_HiltModule.java (generated)

    View Slide

  71. Migration - MainActivity

    View Slide

  72. Migration - Hilt_MainActivity.java (generated)

    View Slide

  73. Migration - Hilt_MainActivity.java (generated)

    View Slide

  74. Migration - Modules

    View Slide

  75. Migration - ViewModelFactory, ViewModelKey

    View Slide

  76. Migration - HiltSampleApplication_HiltComponents.java (generated)

    View Slide

  77. Resources
    ● Dependency injection with Hilt
    ● Dagger Hilt
    ● Using Hilt in your Android app Codelab
    ● Migrating your Dagger app to Hilt Codelab
    ● Scoping in Android and Hilt. Scoping an object A to another object B… | by
    Manuel Vivo | Android Developers
    ● Hilt in multi-module apps
    ● https://github.com/moyheen/HiltSampleApplication
    ● Image by Gerd Altmann from Pixabay

    View Slide

  78. Thank You!
    Moyinoluwa Adeyemi
    @moyheen

    View Slide