Upgrade to Pro — share decks privately, control downloads, hide ads and more …

原理から完全理解するDagger Hilt Migration

原理から完全理解するDagger Hilt Migration

DroidKaigi 2021の発表資料です

Keita Kagurazaka

October 20, 2021
Tweet

More Decks by Keita Kagurazaka

Other Decks in Programming

Transcript

  1. 原理から完全理解する
    Dagger Hilt Migration
    Keita Kagurazaka

    View full-size slide

  2. 本発表について
    ● 話すこと
    ○ 素のDaggerとdagger.android、Hiltの違い
    ○ Hiltへの移行方法
    ○ 移行する際によく当たる問題とその解決方法
    ● 話さないこと / 前提とする知識
    ○ DIとは何か、その必要性
    ○ Daggerの使い方、どんなクラスがあるかなど

    View full-size slide

  3. Agenda
    Daggerの捉え方
    dagger.androidとHiltの思想
    Hiltに段階的に移行する
    FAQ
    01
    02
    03
    04

    View full-size slide

  4. コンセプト
    ● 「段階的」なHilt移行
    ○ 日々のプロジェクトと並行して進められるように
    ● 原理から理解する
    ○ 実際に移行する際に自ら壁を乗り越えられるように

    View full-size slide

  5. Daggerの捉え方
    dagger.androidとHiltの思想
    Hiltに段階的に移行する
    FAQ
    Agenda
    01
    02
    03
    04

    View full-size slide

  6. Dagger = Component Tree

    View full-size slide

  7. Dagger = Component Tree

    View full-size slide

  8. Componentの提供サービス
    1. 依存性(インスタンス)の受け渡し
    2. 依存性(インスタンス)の同一性保証

    View full-size slide

  9. Componentの提供サービス
    1. 依存性(インスタンス)の受け渡し
    2. 依存性(インスタンス)の同一性保証
    = 何回受け渡しても同じインスタンス

    View full-size slide

  10. @Component(modules = [RepositoryModule::class])
    interface ApplicationComponent {
    fun getUserRepository(): UserRepository
    fun inject(app: App)
    }
    Componentによる依存性の受け渡し

    View full-size slide

  11. @Component(modules = [RepositoryModule::class])
    interface ApplicationComponent {
    fun getUserRepository(): UserRepository
    fun inject(app: App)
    }
    Componentによる依存性の受け渡し

    View full-size slide

  12. @Component(modules = [RepositoryModule::class])
    interface ApplicationComponent {
    fun getUserRepository(): UserRepository
    fun inject(app: App)
    }
    Componentによる依存性の受け渡し

    View full-size slide

  13. @Singleton
    @Component(modules = [ApplicationModule::class])
    interface ApplicationComponent { /* 省略 */ }
    @Module
    class ApplicationModule {
    @Singleton
    @Provides
    fun providesThirdPartyLibrary(): ThirdPartyLibrary =
    ThirdPartyLibrary.Builder.build()
    }
    Componentによる同一性保証

    View full-size slide

  14. @Singleton
    @Component(modules = [ApplicationModule::class])
    interface ApplicationComponent { /* 省略 */ }
    @Module
    class ApplicationModule {
    @Singleton
    @Provides
    fun providesThirdPartyLibrary(): ThirdPartyLibrary =
    ThirdPartyLibrary.Builder.build()
    }
    Componentによる同一性保証

    View full-size slide

  15. @Singleton
    @Component(modules = [ApplicationModule::class])
    interface ApplicationComponent { /* 省略 */ }
    @Module
    class ApplicationModule {
    @Singleton
    @Provides
    fun providesThirdPartyLibrary(): ThirdPartyLibrary =
    ThirdPartyLibrary.Builder.build()
    }
    Componentによる同一性保証

    View full-size slide

  16. 倉庫預かりオプション
    ThirdPartyLibrary
    ください
    ThirdParty
    Library
    倉庫
    ApplicationComponent
    以前と同じ
    ブツです

    View full-size slide

  17. @Singleton
    @Component(modules = [ApplicationModule::class])
    interface ApplicationComponent { /* 省略 */ }
    @Module
    class ApplicationModule {
    @Singleton
    @Provides
    fun providesThirdPartyLibrary(): ThirdPartyLibrary =
    ThirdPartyLibrary.Builder.build()
    }
    Componentによる同一性保証

    View full-size slide

  18. @Singleton
    @Component(modules = [ApplicationModule::class])
    interface ApplicationComponent { /* 省略 */ }
    @Module
    class ApplicationModule {
    @Provides
    fun providesThirdPartyLibrary(): ThirdPartyLibrary =
    ThirdPartyLibrary.Builder.build()
    }
    Componentによる同一性保証
    毎回作り直し!

    View full-size slide

  19. Component =
    工場
    倉庫
    受け渡し窓口

    View full-size slide

  20. Dagger = Component Tree

    View full-size slide

  21. インスタンスのライフタイム管理
    アプリのプロセス終了まで
    残ってるよ!
    ThirdParty
    Library
    ApplicationComponentが
    消えるまで残ってるよ!
    倉庫
    ApplicationComponent

    View full-size slide

  22. インスタンスのライフタイム管理
    LoginActivityComponent
    ApplicationComponent
    アプリのプロセス終了まで
    残ってるよ!
    LoginActivityが
    消えるまで残ってるよ!

    View full-size slide

  23. インスタンスのライフタイム管理
    LoginActivityComponent
    ApplicationComponent
    先輩のインスタンスも
    こちらで受け渡します!

    View full-size slide

  24. インスタンスのライフタイム管理
    LoginActivityComponent
    ApplicationComponent
    苦しゅうない
    先輩のインスタンスも
    こちらで受け渡します!

    View full-size slide

  25. インスタンスのライフタイム管理
    LoginActivityComponent
    ApplicationComponent
    Component Tree

    View full-size slide

  26. @Scope
    @Retention(AnnotationRetention.RUNTIME)
    annotation class ActivityScope
    @ActivityScope
    @Subcomponent
    interface LoginActivityComponent {
    fun inject(activity: LoginActivity)
    @Subcomponent.Factory
    interface Factory {
    fun create(): LoginActivityComponent
    }
    }
    SubcomponentによるTree定義

    View full-size slide

  27. @Scope
    @Retention(AnnotationRetention.RUNTIME)
    annotation class ActivityScope
    @ActivityScope
    @Subcomponent
    interface LoginActivityComponent {
    fun inject(activity: LoginActivity)
    @Subcomponent.Factory
    interface Factory {
    fun create(): LoginActivityComponent
    }
    }
    SubcomponentによるTree定義

    View full-size slide

  28. @Module(subcomponents = [LoginActivityComponent::class])
    interface ActivityModule
    @Singleton
    @Component(modules = [
    ApplicationModule::class,
    ActivityModule::class
    ])
    interface ApplicationComponent
    SubcomponentによるTree定義

    View full-size slide

  29. @Module(subcomponents = [LoginActivityComponent::class])
    interface ActivityModule
    @Singleton
    @Component(modules = [
    ApplicationModule::class,
    ActivityModule::class
    ])
    interface ApplicationComponent
    SubcomponentによるTree定義

    View full-size slide

  30. @Module(subcomponents = [LoginActivityComponent::class])
    interface ActivityModule
    @Singleton
    @Component(modules = [
    ApplicationModule::class,
    ActivityModule::class
    ])
    interface ApplicationComponent
    SubcomponentによるTree定義

    View full-size slide

  31. Component Tree
    ApplicationComponent
    ActivityModule
    LoginActivityComponent
    ApplicationModule

    View full-size slide

  32. Component Tree
    ApplicationComponent
    ApplicationModule
    LoginActivityComponent

    View full-size slide

  33. Component Tree
    ApplicationC
    ApplicationM
    LoginActivityC

    View full-size slide

  34. Daggerの捉え方
    dagger.androidとHiltの思想
    Hiltに段階的に移行する
    FAQ
    Agenda
    01
    02
    03
    04

    View full-size slide

  35. interface ApplicationComponent {
    fun loginActivityComponent(): LoginActivityComponent.Factory
    }
    class LoginActivity : AppCompatActivity() {
    lateinit var component: LoginActivityComponent
    override fun onCreate(savedInstanceState: Bundle?) {
    component = (applicationContext as App).component
    .loginActivityComponent().create()
    component.inject(this)
    super.onCreate(savedInstanceState)
    }
    }
    Boilerplate問題

    View full-size slide

  36. interface ApplicationComponent {
    fun loginActivityComponent(): LoginActivityComponent.Factory
    }
    class LoginActivity : AppCompatActivity() {
    lateinit var component: LoginActivityComponent
    override fun onCreate(savedInstanceState: Bundle?) {
    component = (applicationContext as App).component
    .loginActivityComponent().create()
    component.inject(this)
    super.onCreate(savedInstanceState)
    }
    }
    Boilerplate問題

    View full-size slide

  37. dagger.android

    View full-size slide

  38. dagger.android
    書くのが大変なら
    コード生成すればいいじゃない

    View full-size slide

  39. @Module
    interface MainActivityModule {
    @ActivityScope
    @ContributesAndroidInjector
    fun contributesMainActivity(): MainActivity
    }
    @Module(
    includes = [MainActivityModule::class],
    subcomponents = [LoginActivityComponent::class]
    )
    interface ActivityModule
    dagger.androidの解法 - コード生成

    View full-size slide

  40. @Module
    interface MainActivityModule {
    @ActivityScope
    @ContributesAndroidInjector
    fun contributesMainActivity(): MainActivity
    }
    @Module(
    includes = [MainActivityModule::class],
    subcomponents = [LoginActivityComponent::class]
    )
    interface ActivityModule
    dagger.androidの解法 - コード生成

    View full-size slide

  41. @Module
    interface MainActivityModule {
    @ActivityScope
    @ContributesAndroidInjector
    fun contributesMainActivity(): MainActivity
    }
    @Module(
    includes = [MainActivityModule::class],
    subcomponents = [LoginActivityComponent::class]
    )
    interface ActivityModule
    dagger.androidの解法 - コード生成
    MainActivitySubC

    View full-size slide

  42. @Module
    interface MainActivityModule {
    @ActivityScope
    @ContributesAndroidInjector
    fun contributesMainActivity(): MainActivity
    }
    @Module(
    includes = [MainActivityModule::class],
    subcomponents = [LoginActivityComponent::class]
    )
    interface ActivityModule
    dagger.androidの解法 - コード生成

    View full-size slide

  43. @Module
    interface MainActivityModule {
    @ActivityScope
    @ContributesAndroidInjector
    fun contributesMainActivity(): MainActivity
    }
    @Module(
    includes = [MainActivityModule::class],
    subcomponents = [LoginActivityComponent::class]
    )
    interface ActivityModule
    dagger.androidの解法 - コード生成
    MainActivitySubC

    View full-size slide

  44. @Module
    interface MainActivityModule {
    @ActivityScope
    @ContributesAndroidInjector
    fun contributesMainActivity(): MainActivity
    }
    @Module(
    includes = [MainActivityModule::class],
    subcomponents = [LoginActivityComponent::class]
    )
    interface ActivityModule
    dagger.androidの解法 - コード生成
    ApplicationC
    MainActivitySubC

    View full-size slide

  45. @Module(subcomponents =
    MainActivityModule_ContributesMainActivity.MainActivitySubcomponent.class)
    public abstract class MainActivityModule_ContributesMainActivity {
    private MainActivityModule_ContributesMainActivity() {}
    @Binds
    @IntoMap
    @ClassKey(MainActivity.class)
    abstract AndroidInjector.Factory> bindAndroidInjectorFactory(
    MainActivitySubcomponent.Factory builder);
    @Subcomponent
    @ActivityScope
    public interface MainActivitySubcomponent extends AndroidInjector {
    @Subcomponent.Factory
    interface Factory extends AndroidInjector.Factory {}
    }
    }

    View full-size slide

  46. @Module(subcomponents =
    MainActivityModule_ContributesMainActivity.MainActivitySubcomponent.class)
    public abstract class MainActivityModule_ContributesMainActivity {
    private MainActivityModule_ContributesMainActivity() {}
    @Binds
    @IntoMap
    @ClassKey(MainActivity.class)
    abstract AndroidInjector.Factory> bindAndroidInjectorFactory(
    MainActivitySubcomponent.Factory builder);
    @Subcomponent
    @ActivityScope
    public interface MainActivitySubcomponent extends AndroidInjector {
    @Subcomponent.Factory
    interface Factory extends AndroidInjector.Factory {}
    }
    }

    View full-size slide

  47. @Module(subcomponents =
    MainActivityModule_ContributesMainActivity.MainActivitySubcomponent.class)
    public abstract class MainActivityModule_ContributesMainActivity {
    private MainActivityModule_ContributesMainActivity() {}
    @Binds
    @IntoMap
    @ClassKey(MainActivity.class)
    abstract AndroidInjector.Factory> bindAndroidInjectorFactory(
    MainActivitySubcomponent.Factory builder);
    @Subcomponent
    @ActivityScope
    public interface MainActivitySubcomponent extends AndroidInjector {
    @Subcomponent.Factory
    interface Factory extends AndroidInjector.Factory {}
    }
    }

    View full-size slide

  48. @Module(subcomponents =
    MainActivityModule_ContributesMainActivity.MainActivitySubcomponent.class)
    public abstract class MainActivityModule_ContributesMainActivity {
    private MainActivityModule_ContributesMainActivity() {}
    @Binds
    @IntoMap
    @ClassKey(MainActivity.class)
    abstract AndroidInjector.Factory> bindAndroidInjectorFactory(
    MainActivitySubcomponent.Factory builder);
    @Subcomponent
    @ActivityScope
    public interface MainActivitySubcomponent extends AndroidInjector {
    @Subcomponent.Factory
    interface Factory extends AndroidInjector.Factory {}
    }
    }

    View full-size slide

  49. @Module(subcomponents =
    MainActivityModule_ContributesMainActivity.MainActivitySubcomponent.class)
    public abstract class MainActivityModule_ContributesMainActivity {
    private MainActivityModule_ContributesMainActivity() {}
    @Binds
    @IntoMap
    @ClassKey(MainActivity.class)
    abstract AndroidInjector.Factory> bindAndroidInjectorFactory(
    MainActivitySubcomponent.Factory builder);
    @Subcomponent
    @ActivityScope
    public interface MainActivitySubcomponent extends AndroidInjector {
    @Subcomponent.Factory
    interface Factory extends AndroidInjector.Factory {}
    }
    }
    MainActivity

    View full-size slide

  50. @Module(subcomponents =
    MainActivityModule_ContributesMainActivity.MainActivitySubcomponent.class)
    public abstract class MainActivityModule_ContributesMainActivity {
    private MainActivityModule_ContributesMainActivity() {}
    @Binds
    @IntoMap
    @ClassKey(MainActivity.class)
    abstract AndroidInjector.Factory> bindAndroidInjectorFactory(
    MainActivitySubcomponent.Factory builder);
    @Subcomponent
    @ActivityScope
    public interface MainActivitySubcomponent extends AndroidInjector {
    @Subcomponent.Factory
    interface Factory extends AndroidInjector.Factory {}
    }
    }
    MainActivitySubC
    MainActivity

    View full-size slide

  51. @Module(subcomponents =
    MainActivityModule_ContributesMainActivity.MainActivitySubcomponent.class)
    public abstract class MainActivityModule_ContributesMainActivity {
    private MainActivityModule_ContributesMainActivity() {}
    @Binds
    @IntoMap
    @ClassKey(MainActivity.class)
    abstract AndroidInjector.Factory> bindAndroidInjectorFactory(
    MainActivitySubcomponent.Factory builder);
    @Subcomponent
    @ActivityScope
    public interface MainActivitySubcomponent extends AndroidInjector {
    @Subcomponent.Factory
    interface Factory extends AndroidInjector.Factory {}
    }
    }
    MainActivitySubC
    MainActivity

    View full-size slide

  52. dagger.androidのinjection

    View full-size slide

  53. dagger.androidのinjection
    1. AndroidInjection.inject

    View full-size slide

  54. dagger.androidのinjection
    1. AndroidInjection.inject
    DispatchingAndroidInjector

    View full-size slide

  55. dagger.androidのinjection
    1. AndroidInjection.inject
    DispatchingAndroidInjector

    MainActivitySubC
    MainActivity
    MainFragmentSubC
    MainFragment
    LoginActivitySubC
    LoginActivity

    View full-size slide

  56. dagger.androidのinjection
    1. AndroidInjection.inject
    2. 渡されたインスタンスのクラスを
    keyにSubCを見つける
    DispatchingAndroidInjector

    MainActivitySubC
    MainActivity
    MainFragmentSubC
    MainFragment
    LoginActivitySubC
    LoginActivity

    View full-size slide

  57. dagger.androidのinjection
    1. AndroidInjection.inject
    2. 渡されたインスタンスのクラスを
    keyにSubCを見つける
    3. SubCをインスタンス化して
    injection
    DispatchingAndroidInjector

    MainActivitySubC
    MainActivity
    MainFragmentSubC
    MainFragment
    LoginActivitySubC
    LoginActivity

    View full-size slide

  58. class App : Application(), HasAndroidInjector {
    @Inject
    lateinit var androidInjector: DispatchingAndroidInjector
    override fun androidInjector(): AndroidInjector =
    androidInjector
    }
    dagger.androidのinjection

    View full-size slide

  59. dagger.androidのComponent Tree
    ApplicationC
    MainActivitySubC
    MainFragmentSubC HogeFragmentSubC
    HugaActivitySubC
    HugaFragmentSubC PiyoFragmentSubC

    View full-size slide

  60. Hilt
    Componentは
    こっちで用意するから
    それ使ってね

    View full-size slide

  61. https://dagger.dev/hilt/components より引用
    Hiltの解法 - プリセットComponent

    View full-size slide

  62. HiltのComponent Tree (simple ver.)
    SingletonC ActivityC FragmentC

    View full-size slide

  63. @InstallIn(SingletonComponent::class)
    @Module
    class ApplicationModule {
    @Singleton
    @Provides
    fun providesThirdPartyLibrary(): ThirdPartyLibrary =
    ThirdPartyLibrary.Builder.build()
    }
    プリセットComponentの使い方

    View full-size slide

  64. @InstallIn(SingletonComponent::class)
    @Module
    class ApplicationModule {
    @Singleton
    @Provides
    fun providesThirdPartyLibrary(): ThirdPartyLibrary =
    ThirdPartyLibrary.Builder.build()
    }
    プリセットComponentの使い方

    View full-size slide

  65. @InstallIn(SingletonComponent::class)
    @Module
    class ApplicationModule
    @Singleton
    @Component(modules = [ApplicationModule::class])
    interface ApplicationComponent
    プリセットComponentの使い方

    View full-size slide

  66. @Component(modules = [AllApplicationModule::class])
    interface ApplicationComponent {
    fun inject(instance: NeedInjectionClass)
    fun getUserRepository(): UserRepository
    }
    インスタンス受け渡し窓口閉鎖のご連絡

    View full-size slide

  67. @InstallIn(SingletonComponent::class)
    @EntryPoint
    interface ApplicationEntryPoint {
    fun inject(instance: NeedInjectionClass)
    fun getUserRepository(): UserRepository
    }
    EntryPoint

    View full-size slide

  68. @InstallIn(SingletonComponent::class)
    @EntryPoint
    interface ApplicationEntryPoint {
    fun inject(instance: NeedInjectionClass)
    fun getUserRepository(): UserRepository
    }
    public abstract static class SingletonC implements SingletonComponent,
    ApplicationEntryPoint {
    /* 省略 */
    }
    EntryPoint = Componentの1窓口

    View full-size slide

  69. interface RepositoryProvider {
    fun getUserRepository(): UserRepository
    }
    interface ApplicationInjector {
    fun inject(instance: NeedInjectionClass)
    }
    @Singleton
    @Component(modules = [ApplicationModule::class])
    interface ApplicationComponent : RepositoryProvider, ApplicationInjector
    EntryPoint ≈ Componentの分割定義

    View full-size slide

  70. class MainActivity : AppCompatActivity()
    class MainFragment : Fragment()
    HiltのInjection

    View full-size slide

  71. @AndroidEntryPoint
    class MainActivity : AppCompatActivity()
    @AndroidEntryPoint
    class MainFragment : Fragment()
    HiltのInjection

    View full-size slide

  72. public abstract class Hilt_MainActivity extends AppCompatActivity
    implements GeneratedComponentManagerHolder {
    Hilt_MainActivity() {
    super();
    _initHiltInternal();
    }
    private void _initHiltInternal() {
    addOnContextAvailableListener(new OnContextAvailableListener() {
    @Override
    public void onContextAvailable(Context context) {
    inject();
    }
    });
    }
    }

    View full-size slide

  73. public abstract class Hilt_MainActivity extends AppCompatActivity
    implements GeneratedComponentManagerHolder {
    Hilt_MainActivity() {
    super();
    _initHiltInternal();
    }
    private void _initHiltInternal() {
    addOnContextAvailableListener(new OnContextAvailableListener() {
    @Override
    public void onContextAvailable(Context context) {
    inject();
    }
    });
    }
    }

    View full-size slide

  74. public abstract class Hilt_MainActivity extends AppCompatActivity
    implements GeneratedComponentManagerHolder {
    Hilt_MainActivity() {
    super();
    _initHiltInternal();
    }
    private void _initHiltInternal() {
    addOnContextAvailableListener(new OnContextAvailableListener() {
    @Override
    public void onContextAvailable(Context context) {
    inject();
    }
    });
    }
    }
    ボイラープレート
    全部ここでやります

    View full-size slide

  75. public abstract class Hilt_MainActivity extends AppCompatActivity
    implements GeneratedComponentManagerHolder {
    Hilt_MainActivity() {
    super();
    _initHiltInternal();
    }
    private void _initHiltInternal() {
    addOnContextAvailableListener(new OnContextAvailableListener() {
    @Override
    public void onContextAvailable(Context context) {
    inject();
    }
    });
    }
    }

    View full-size slide

  76. @AndroidEntryPoint
    class MainActivity : AppCompatActivity()
    Bytecode transformation
    in .kt

    View full-size slide

  77. @AndroidEntryPoint
    class MainActivity : AppCompatActivity()
    Bytecode transformation
    in .kt

    View full-size slide

  78. @AndroidEntryPoint
    class MainActivity : Hilt_MainActivity()
    Bytecode transformation
    in .dex

    View full-size slide

  79. @AndroidEntryPoint
    class MainActivity : AppCompatActivity()
    @AndroidEntryPoint
    class MainFragment : Fragment()
    HiltのInjection

    View full-size slide

  80. Daggerの捉え方
    dagger.androidとHiltの思想
    Hiltに段階的に移行する
    FAQ
    Agenda
    01
    02
    03
    04

    View full-size slide

  81. サンプルプロジェクト
    ● https://github.com/k-kagurazaka/hilt-migration
    ● main branch
    ○ raw Dagger + dagger.androidの構成
    ● hilt-migration branch
    ○ Hilt化済み
    ○ 各commitはbuild pass

    View full-size slide

  82. サンプルプロジェクトのComponent Tree
    ApplicationC
    MainActivityC MainFragmentC
    LoginActivityC LoginFragmentC
    EmailSignupActivityC UserRegistrationFragmentC
    SnsSignupActivityC UserRegistrationFragmentC

    View full-size slide

  83. raw Dagger
    ApplicationC
    MainActivityC MainFragmentC
    LoginActivityC LoginFragmentC
    EmailSignupActivityC UserRegistrationFragmentC
    SnsSignupActivityC UserRegistrationFragmentC

    View full-size slide

  84. dagger.android
    ApplicationC
    MainActivityC MainFragmentC
    LoginActivityC LoginFragmentC
    EmailSignupActivityC UserRegistrationFragmentC
    SnsSignupActivityC UserRegistrationFragmentC

    View full-size slide

  85. 同一Fragment、別Component
    ApplicationC
    MainActivityC MainFragmentC
    LoginActivityC LoginFragmentC
    EmailSignupActivityC UserRegistrationFragmentC
    SnsSignupActivityC UserRegistrationFragmentC

    View full-size slide

  86. 移行スタート

    View full-size slide

  87. 移行ステップ
    1. gradleの設定
    2. Component Treeの接ぎ木
    3. Scopeアノテーションの置き換え
    4. MainActivityの移行
    5. Loginの移行
    6. MainFragmentの移行
    7. Activityのお掃除
    8. dagger.androidの除去
    9. ApplicationComponentの削除

    View full-size slide

  88. 移行ステップ
    1. gradleの設定
    2. Component Treeの接ぎ木
    3. Scopeアノテーションの置き換え
    4. MainActivityの移行
    5. Loginの移行
    6. MainFragmentの移行
    7. Activityのお掃除
    8. dagger.androidの除去
    9. ApplicationComponentの削除

    View full-size slide

  89. 移行ステップ
    1. gradleの設定
    2. Component Treeの接ぎ木
    3. Scopeアノテーションの置き換え
    4. MainActivityの移行
    5. Loginの移行
    6. MainFragmentの移行
    7. Activityのお掃除
    8. dagger.androidの除去
    9. ApplicationComponentの削除

    View full-size slide

  90. Component Treeの接ぎ木
    ApplicationC
    MainActivityC MainFragmentC
    LoginActivityC LoginFragmentC
    SingletonC ActivityC FragmentC
    ApplicationM
    RepositoryM

    View full-size slide

  91. Component Treeの接ぎ木
    ApplicationC
    MainActivityC MainFragmentC
    LoginActivityC LoginFragmentC
    SingletonC ActivityC FragmentC
    ApplicationM
    RepositoryM

    View full-size slide

  92. @Singleton
    @Component(modules = [
    AndroidInjectionModule::class,
    ApplicationModule::class,
    RepositoryModule::class,
    ActivityModule::class,])
    interface ApplicationComponent : AndroidInjector {
    fun loginActivityComponent(): LoginActivityComponent.Factory
    @Component.Factory
    interface Factory {
    fun create(@BindsInstance app: App): ApplicationComponent
    }
    }
    Component Treeの接ぎ木

    View full-size slide

  93. @Singleton
    @Component(modules = [
    AndroidInjectionModule::class,
    ApplicationModule::class,
    RepositoryModule::class,
    ActivityModule::class,])
    interface ApplicationComponent : AndroidInjector {
    fun loginActivityComponent(): LoginActivityComponent.Factory
    @Component.Factory
    interface Factory {
    fun create(@BindsInstance app: App): ApplicationComponent
    }
    }
    Component Treeの接ぎ木

    View full-size slide

  94. @Singleton
    @Component(modules = [
    AndroidInjectionModule::class,
    ApplicationModule::class,
    RepositoryModule::class,
    ActivityModule::class,])
    interface ApplicationComponent : AndroidInjector {
    fun loginActivityComponent(): LoginActivityComponent.Factory
    }
    Component Treeの接ぎ木

    View full-size slide

  95. @Singleton
    @Component(modules = [
    AndroidInjectionModule::class,
    ApplicationModule::class,
    RepositoryModule::class,
    ActivityModule::class,])
    @InstallIn(SingletonComponent::class)
    @EntryPoint
    interface ApplicationComponent : AndroidInjector {
    fun loginActivityComponent(): LoginActivityComponent.Factory
    }
    Component Treeの接ぎ木

    View full-size slide

  96. @Singleton
    @Component(modules = [
    AndroidInjectionModule::class,
    ApplicationModule::class,
    RepositoryModule::class,
    ActivityModule::class,])
    @InstallIn(SingletonComponent::class)
    @EntryPoint
    interface ApplicationComponent : AndroidInjector {
    fun loginActivityComponent(): LoginActivityComponent.Factory
    }
    Component Treeの接ぎ木

    View full-size slide

  97. SingletonCImpl
    Component Treeの接ぎ木
    ApplicationC
    MainActivityC MainFragmentC
    LoginActivityC LoginFragmentC
    SingletonC ActivityC FragmentC
    ApplicationM
    RepositoryM

    View full-size slide

  98. SingletonCImpl
    Component Treeの接ぎ木
    ApplicationC
    MainActivityC MainFragmentC
    LoginActivityC LoginFragmentC
    SingletonC ActivityC FragmentC
    ApplicationM
    RepositoryM

    View full-size slide

  99. @Singleton
    @Component(modules = [
    AndroidInjectionModule::class,
    ApplicationModule::class,
    RepositoryModule::class,
    ActivityModule::class,])
    @InstallIn(SingletonComponent::class)
    @EntryPoint
    interface ApplicationComponent : AndroidInjector {
    fun loginActivityComponent(): LoginActivityComponent.Factory
    }
    Component Treeの接ぎ木

    View full-size slide

  100. @InstallIn(SingletonComponent::class)
    @Module(includes = [
    AndroidInjectionModule::class,
    ApplicationModule::class,
    RepositoryModule::class,
    ActivityModule::class,])
    interface AggregatorModule
    @InstallIn(SingletonComponent::class)
    @EntryPoint
    interface ApplicationComponent : AndroidInjector {
    fun loginActivityComponent(): LoginActivityComponent.Factory
    }
    Component Treeの接ぎ木

    View full-size slide

  101. SingletonCImpl
    Component Treeの接ぎ木
    ApplicationC
    MainActivityC MainFragmentC
    LoginActivityC LoginFragmentC
    SingletonC ActivityC FragmentC
    ApplicationM
    RepositoryM

    View full-size slide

  102. class App : Application(), HasAndroidInjector {
    val component: ApplicationComponent =
    DaggerApplicationComponent.factory().create(this)
    @Inject
    lateinit var androidInjector: DispatchingAndroidInjector
    override fun androidInjector(): AndroidInjector =
    androidInjector
    }
    Applicationの変更

    View full-size slide

  103. class App : Application(), HasAndroidInjector {
    val component: ApplicationComponent =
    DaggerApplicationComponent.factory().create(this)
    @Inject
    lateinit var androidInjector: DispatchingAndroidInjector
    override fun androidInjector(): AndroidInjector =
    androidInjector
    }
    Applicationの変更

    View full-size slide

  104. class App : Application(), HasAndroidInjector {
    val component: ApplicationComponent by lazy {
    EntryPoints.get(this, ApplicationComponent::class.java)
    }
    @Inject
    lateinit var androidInjector: DispatchingAndroidInjector
    override fun androidInjector(): AndroidInjector =
    androidInjector
    }
    Applicationの変更

    View full-size slide

  105. class App : Application(), HasAndroidInjector {
    val component: ApplicationComponent by lazy {
    EntryPoints.get(this, ApplicationComponent::class.java)
    }
    @Inject
    lateinit var androidInjector: DispatchingAndroidInjector
    override fun androidInjector(): AndroidInjector =
    androidInjector
    }
    Applicationの変更

    View full-size slide

  106. @HiltAndroidApp
    class App : Application(), HasAndroidInjector {
    val component: ApplicationComponent by lazy {
    EntryPoints.get(this, ApplicationComponent::class.java)
    }
    @Inject
    lateinit var androidInjector: DispatchingAndroidInjector
    override fun androidInjector(): AndroidInjector =
    androidInjector
    }
    Applicationの変更

    View full-size slide

  107. @HiltAndroidApp
    class App : Application(), HasAndroidInjector {
    val component: ApplicationComponent by lazy {
    EntryPoints.get(this, ApplicationComponent::class.java)
    }
    @Inject
    lateinit var androidInjector: DispatchingAndroidInjector
    override fun androidInjector(): AndroidInjector =
    androidInjector
    }
    Applicationの変更

    View full-size slide

  108. @HiltAndroidApp
    class App : Application(), HasAndroidInjector {
    val component: ApplicationComponent by lazy {
    EntryPoints.get(this, ApplicationComponent::class.java)
    }
    @Inject
    lateinit var androidInjector: DispatchingAndroidInjector
    override fun androidInjector(): AndroidInjector =
    androidInjector
    }
    Applicationの変更

    View full-size slide

  109. SingletonCImpl
    現時点のComponent Tree
    ApplicationC
    MainActivityC MainFragmentC
    LoginActivityC LoginFragmentC
    SingletonC ActivityC FragmentC
    ApplicationM
    RepositoryM

    View full-size slide

  110. 現時点のComponent Tree
    ApplicationC
    MainActivityC MainFragmentC
    LoginActivityC LoginFragmentC
    SingletonC ActivityC FragmentC
    ApplicationM
    RepositoryM

    View full-size slide

  111. 移行ステップ
    1. gradleの設定
    2. Component Treeの接ぎ木
    3. Scopeアノテーションの置き換え
    4. MainActivityの移行
    5. Loginの移行
    6. MainFragmentの移行
    7. Activityのお掃除
    8. dagger.androidの除去
    9. ApplicationComponentの削除

    View full-size slide

  112. @Module
    interface MainActivityModule {
    @ActivityScope
    @ContributesAndroidInjector(modules = [MainFragmentModule::class])
    fun contributesMainActivity(): MainActivity
    }
    @Module
    interface MainFragmentModule {
    @FragmentScope
    @ContributesAndroidInjector
    fun contributesMainFragment(): MainFragment
    }
    Scopeアノテーションの置き換え

    View full-size slide

  113. @Module
    interface MainActivityModule {
    @ActivityScope
    @ContributesAndroidInjector(modules = [MainFragmentModule::class])
    fun contributesMainActivity(): MainActivity
    }
    @Module
    interface MainFragmentModule {
    @FragmentScope
    @ContributesAndroidInjector
    fun contributesMainFragment(): MainFragment
    }
    Scopeアノテーションの置き換え

    View full-size slide

  114. import dagger.hilt.android.scopes.ActivityScoped
    import dagger.hilt.android.scopes.FragmentScoped
    @Module
    interface MainActivityModule {
    @ActivityScoped
    @ContributesAndroidInjector(modules = [MainFragmentModule::class])
    fun contributesMainActivity(): MainActivity
    }
    @Module
    interface MainFragmentModule {
    @FragmentScoped
    @ContributesAndroidInjector
    fun contributesMainFragment(): MainFragment
    }
    Scopeアノテーションの置き換え

    View full-size slide

  115. import dagger.hilt.android.scopes.ActivityScoped
    import dagger.hilt.android.scopes.FragmentScoped
    @Module
    interface MainActivityModule {
    @ActivityScoped
    @ContributesAndroidInjector(modules = [MainFragmentModule::class])
    fun contributesMainActivity(): MainActivity
    }
    @Module
    interface MainFragmentModule {
    @FragmentScoped
    @ContributesAndroidInjector
    fun contributesMainFragment(): MainFragment
    }
    Scopeアノテーションの置き換え

    View full-size slide

  116. 移行ステップ
    1. gradleの設定
    2. Component Treeの接ぎ木
    3. Scopeアノテーションの置き換え
    4. MainActivityの移行
    5. Loginの移行
    6. MainFragmentの移行
    7. Activityのお掃除
    8. dagger.androidの除去
    9. ApplicationComponentの削除

    View full-size slide

  117. 移行 = 担当Component変更
    ApplicationC
    MainActivityC MainFragmentC
    LoginActivityC LoginFragmentC
    SingletonC ActivityC FragmentC

    View full-size slide

  118. 移行 = 担当Component変更
    ApplicationC
    MainActivityC MainFragmentC
    LoginActivityC LoginFragmentC
    SingletonC ActivityC FragmentC
    MainActivity担当

    View full-size slide

  119. 移行 = 担当Component変更
    ApplicationC
    MainActivityC MainFragmentC
    LoginActivityC LoginFragmentC
    SingletonC ActivityC FragmentC
    MainActivity担当

    View full-size slide

  120. class MainActivity : AppCompatActivity(), HasAndroidInjector {
    @Inject
    lateinit var androidInjector: DispatchingAndroidInjector
    override fun androidInjector(): AndroidInjector = androidInjector
    override fun onCreate(savedInstanceState: Bundle?) {
    AndroidInjection.inject(this)
    super.onCreate(savedInstanceState)
    }
    }
    MainActivityの移行

    View full-size slide

  121. class MainActivity : AppCompatActivity(), HasAndroidInjector {
    @Inject
    lateinit var androidInjector: DispatchingAndroidInjector
    override fun androidInjector(): AndroidInjector = androidInjector
    override fun onCreate(savedInstanceState: Bundle?) {
    AndroidInjection.inject(this)
    super.onCreate(savedInstanceState)
    }
    }
    MainActivityの移行

    View full-size slide

  122. class MainActivity : AppCompatActivity(), HasAndroidInjector {
    @Inject
    lateinit var androidInjector: DispatchingAndroidInjector
    override fun androidInjector(): AndroidInjector = androidInjector
    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    }
    }
    MainActivityの移行

    View full-size slide

  123. @AndroidEntryPoint
    class MainActivity : AppCompatActivity(), HasAndroidInjector {
    @Inject
    lateinit var androidInjector: DispatchingAndroidInjector
    override fun androidInjector(): AndroidInjector = androidInjector
    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    }
    }
    MainActivityの移行

    View full-size slide

  124. @AndroidEntryPoint
    class MainActivity : AppCompatActivity(), HasAndroidInjector {
    @Inject
    lateinit var androidInjector: DispatchingAndroidInjector
    override fun androidInjector(): AndroidInjector = androidInjector
    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    }
    }
    MainActivityの移行

    View full-size slide

  125. MainActivityのComponent削除
    ApplicationC
    MainActivityC MainFragmentC
    LoginActivityC LoginFragmentC
    SingletonC ActivityC FragmentC

    View full-size slide

  126. MainActivityのComponent削除
    ApplicationC
    MainFragmentC
    LoginActivityC LoginFragmentC
    SingletonC ActivityC FragmentC

    View full-size slide

  127. MainFragmentのComponent接ぎ木
    ApplicationC
    MainFragmentC
    LoginActivityC LoginFragmentC
    SingletonC ActivityC FragmentC

    View full-size slide

  128. MainFragmentのComponent接ぎ木
    ApplicationC
    MainFragmentC
    LoginActivityC LoginFragmentC
    SingletonC ActivityC FragmentC

    View full-size slide

  129. MainFragmentのComponent接ぎ木
    ApplicationC
    MainFragmentC
    LoginActivityC LoginFragmentC
    SingletonC ActivityC FragmentC
    MainFragmentModule

    View full-size slide

  130. @InstallIn(ActivityComponent::class)
    @Module(
    includes = [
    MainFragmentModule::class,
    ]
    )
    interface DaggerAndroidActivityModule
    MainFragmentのComponent接ぎ木

    View full-size slide

  131. 移行ステップ
    1. gradleの設定
    2. Component Treeの接ぎ木
    3. Scopeアノテーションの置き換え
    4. MainActivityの移行
    5. Loginの移行
    6. MainFragmentの移行
    7. Activityのお掃除
    8. dagger.androidの除去
    9. ApplicationComponentの削除

    View full-size slide

  132. Loginの移行
    ApplicationC
    MainFragmentC
    LoginActivityC LoginFragmentC
    SingletonC ActivityC FragmentC

    View full-size slide

  133. Loginの移行
    ApplicationC
    MainFragmentC
    LoginActivityC LoginFragmentC
    SingletonC ActivityC FragmentC

    View full-size slide

  134. Loginの移行
    ApplicationC
    MainFragmentC
    LoginActivityC LoginFragmentC
    SingletonC ActivityC FragmentC
    LoginActivity担当 LoginFragment担当

    View full-size slide

  135. Loginの移行
    ApplicationC
    MainFragmentC
    LoginActivityC LoginFragmentC
    SingletonC ActivityC FragmentC
    LoginActivity担当 LoginFragment担当

    View full-size slide

  136. class LoginActivity : AppCompatActivity() {
    lateinit var component: LoginActivityComponent
    override fun onCreate(savedInstanceState: Bundle?) {
    component = (applicationContext as App).component
    .loginActivityComponent().create(this)
    component.inject(this)
    super.onCreate(savedInstanceState)
    }
    }
    LoginActivityの移行

    View full-size slide

  137. class LoginActivity : AppCompatActivity() {
    lateinit var component: LoginActivityComponent
    override fun onCreate(savedInstanceState: Bundle?) {
    component = (applicationContext as App).component
    .loginActivityComponent().create(this)
    component.inject(this)
    super.onCreate(savedInstanceState)
    }
    }
    LoginActivityの移行

    View full-size slide

  138. class LoginActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    }
    }
    LoginActivityの移行

    View full-size slide

  139. @AndroidEntryPoint
    class LoginActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    }
    }
    LoginActivityの移行

    View full-size slide

  140. @AndroidEntryPoint
    class LoginActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    /* 省略 */
    }
    }
    LoginActivityの移行

    View full-size slide

  141. class LoginFragment: Fragment() {
    lateinit var component: LoginFragmentComponent
    override fun onAttach(context: Context) {
    super.onAttach(context)
    component = (requireActivity() as LoginActivity).component
    .loginFragmentComponent().create(this)
    component.inject(this)
    }
    }
    LoginFragmentの移行

    View full-size slide

  142. class LoginFragment: Fragment() {
    lateinit var component: LoginFragmentComponent
    override fun onAttach(context: Context) {
    super.onAttach(context)
    component = (requireActivity() as LoginActivity).component
    .loginFragmentComponent().create(this)
    component.inject(this)
    }
    }
    LoginFragmentの移行

    View full-size slide

  143. class LoginFragment: Fragment() {
    override fun onAttach(context: Context) {
    super.onAttach(context)
    }
    }
    LoginFragmentの移行

    View full-size slide

  144. @AndroidEntryPoint
    class LoginFragment: Fragment() {
    override fun onAttach(context: Context) {
    super.onAttach(context)
    }
    }
    LoginFragmentの移行

    View full-size slide

  145. @AndroidEntryPoint
    class LoginFragment: Fragment() {
    override fun onAttach(context: Context) {
    super.onAttach(context)
    /* 省略 */
    }
    }
    LoginFragmentの移行

    View full-size slide

  146. LoginのComponent削除
    ApplicationC
    MainFragmentC
    LoginActivityC LoginFragmentC
    SingletonC ActivityC FragmentC

    View full-size slide

  147. LoginのComponent削除
    ApplicationC
    MainFragmentC
    SingletonC ActivityC FragmentC

    View full-size slide

  148. LoginのComponent削除
    ApplicationC
    MainFragmentC
    SingletonC ActivityC FragmentC

    View full-size slide

  149. 移行ステップ
    1. gradleの設定
    2. Component Treeの接ぎ木
    3. Scopeアノテーションの置き換え
    4. MainActivityの移行
    5. Loginの移行
    6. MainFragmentの移行
    7. Activityのお掃除
    8. dagger.androidの除去
    9. ApplicationComponentの削除

    View full-size slide

  150. MainFragmentの移行
    ApplicationC
    MainFragmentC
    SingletonC ActivityC FragmentC
    MainFragment担当

    View full-size slide

  151. MainFragmentの移行
    ApplicationC
    MainFragmentC
    SingletonC ActivityC FragmentC
    MainFragment担当

    View full-size slide

  152. class MainFragment : Fragment() {
    override fun onAttach(context: Context) {
    AndroidSupportInjection.inject(this)
    super.onAttach(context)
    /* 省略 */
    }
    }
    MainFragmentの移行

    View full-size slide

  153. class MainFragment : Fragment() {
    override fun onAttach(context: Context) {
    AndroidSupportInjection.inject(this)
    super.onAttach(context)
    /* 省略 */
    }
    }
    MainFragmentの移行

    View full-size slide

  154. class MainFragment : Fragment() {
    override fun onAttach(context: Context) {
    super.onAttach(context)
    /* 省略 */
    }
    }
    MainFragmentの移行

    View full-size slide

  155. @AndroidEntryPoint
    class MainFragment : Fragment() {
    override fun onAttach(context: Context) {
    super.onAttach(context)
    /* 省略 */
    }
    }
    MainFragmentの移行

    View full-size slide

  156. @AndroidEntryPoint
    class MainFragment : Fragment() {
    override fun onAttach(context: Context) {
    super.onAttach(context)
    /* 省略 */
    }
    }
    MainFragmentの移行

    View full-size slide

  157. MainFragmentのComponent削除
    ApplicationC
    MainFragmentC
    SingletonC ActivityC FragmentC

    View full-size slide

  158. MainFragmentのComponent削除
    ApplicationC
    SingletonC ActivityC FragmentC

    View full-size slide

  159. 移行ステップ
    1. gradleの設定
    2. Component Treeの接ぎ木
    3. Scopeアノテーションの置き換え
    4. MainActivityの移行
    5. Loginの移行
    6. MainFragmentの移行
    7. Activityのお掃除
    8. dagger.androidの除去
    9. ApplicationComponentの削除

    View full-size slide

  160. @AndroidEntryPoint
    class MainActivity : AppCompatActivity(), HasAndroidInjector {
    @Inject
    lateinit var androidInjector: DispatchingAndroidInjector
    override fun androidInjector(): AndroidInjector = androidInjector
    /* 省略 */
    }
    MainActivityからdagger.androidを除去

    View full-size slide

  161. @AndroidEntryPoint
    class MainActivity : AppCompatActivity(), HasAndroidInjector {
    @Inject
    lateinit var androidInjector: DispatchingAndroidInjector
    override fun androidInjector(): AndroidInjector = androidInjector
    /* 省略 */
    }
    MainActivityからdagger.androidを除去

    View full-size slide

  162. @AndroidEntryPoint
    class MainActivity : AppCompatActivity() {
    /* 省略 */
    }
    MainActivityからdagger.androidを除去

    View full-size slide

  163. 移行ステップ
    1. gradleの設定
    2. Component Treeの接ぎ木
    3. Scopeアノテーションの置き換え
    4. MainActivityの移行
    5. Loginの移行
    6. MainFragmentの移行
    7. Activityのお掃除
    8. dagger.androidの除去
    9. ApplicationComponentの削除

    View full-size slide

  164. Activity / Fragmentの移行が完了したら
    ApplicationC
    SingletonC ActivityC FragmentC

    View full-size slide

  165. @HiltAndroidApp
    class App : Application(), HasAndroidInjector {
    @Inject
    lateinit var androidInjector: DispatchingAndroidInjector
    override fun androidInjector(): AndroidInjector =
    androidInjector
    val component: ApplicationComponent by lazy {
    EntryPoints.get(this, ApplicationComponent::class.java)
    }
    }
    Appからdagger.androidを除去

    View full-size slide

  166. @HiltAndroidApp
    class App : Application(), HasAndroidInjector {
    @Inject
    lateinit var androidInjector: DispatchingAndroidInjector
    override fun androidInjector(): AndroidInjector =
    androidInjector
    val component: ApplicationComponent by lazy {
    EntryPoints.get(this, ApplicationComponent::class.java)
    }
    }
    Appからdagger.androidを除去

    View full-size slide

  167. @HiltAndroidApp
    class App : Application() {
    val component: ApplicationComponent by lazy {
    EntryPoints.get(this, ApplicationComponent::class.java)
    }
    }
    Appからdagger.androidを除去

    View full-size slide

  168. @InstallIn(SingletonComponent::class)
    @EntryPoint
    interface ApplicationComponent : AndroidInjector
    @InstallIn(SingletonComponent::class)
    @Module(
    includes = [
    AndroidInjectionModule::class,
    ApplicationModule::class,
    RepositoryModule::class,
    ]
    )
    interface AggregatorModule
    Component Treeからdagger.androidを除去

    View full-size slide

  169. @InstallIn(SingletonComponent::class)
    @EntryPoint
    interface ApplicationComponent : AndroidInjector
    @InstallIn(SingletonComponent::class)
    @Module(
    includes = [
    AndroidInjectionModule::class,
    ApplicationModule::class,
    RepositoryModule::class,
    ]
    )
    interface AggregatorModule
    Component Treeからdagger.androidを除去

    View full-size slide

  170. @InstallIn(SingletonComponent::class)
    @EntryPoint
    interface ApplicationComponent
    @InstallIn(SingletonComponent::class)
    @Module(
    includes = [
    ApplicationModule::class,
    RepositoryModule::class,
    ]
    )
    interface AggregatorModule
    Component Treeからdagger.androidを除去

    View full-size slide

  171. 移行ステップ
    1. gradleの設定
    2. Component Treeの接ぎ木
    3. Scopeアノテーションの置き換え
    4. MainActivityの移行
    5. Loginの移行
    6. MainFragmentの移行
    7. Activityのお掃除
    8. dagger.androidの除去
    9. ApplicationComponentの削除

    View full-size slide

  172. ApplicationComponent削除
    ApplicationC
    SingletonC ActivityC FragmentC

    View full-size slide

  173. ApplicationComponent削除
    ApplicationC
    SingletonC ActivityC FragmentC

    View full-size slide

  174. @HiltAndroidApp
    class App : Application() {
    val component: ApplicationComponent by lazy {
    EntryPoints.get(this, ApplicationComponent::class.java)
    }
    }
    @InstallIn(SingletonComponent::class)
    @EntryPoint
    interface ApplicationComponent
    ApplicationComponentを削除

    View full-size slide

  175. @HiltAndroidApp
    class App : Application() {
    val component: ApplicationComponent by lazy {
    EntryPoints.get(this, ApplicationComponent::class.java)
    }
    }
    @InstallIn(SingletonComponent::class)
    @EntryPoint
    interface ApplicationComponent
    ApplicationComponentを削除

    View full-size slide

  176. @HiltAndroidApp
    class App : Application() {
    /* 省略 */
    }
    ApplicationComponentを削除

    View full-size slide

  177. Well done 👏

    View full-size slide

  178. Daggerの捉え方
    dagger.androidとHiltの思想
    Hiltに段階的に移行する
    FAQ
    Agenda
    01
    02
    03
    04

    View full-size slide

  179. Q. LoginActivityのような具象クラスを扱いたい
    @ActivityScope
    @Subcomponent
    interface LoginActivityComponent {
    @Subcomponent.Factory
    interface Factory {
    fun create(@BindsInstance activity: LoginActivity): LoginActivityComponent
    }
    }

    View full-size slide

  180. Q. LoginActivityのような具象クラスを扱いたい
    A. castしましょう
    @InstallIn(ActivityComponent::class)
    @Module
    class LoginActivityModule {
    @Provides
    fun providesLoginActivity(activity: Activity): LoginActivity? =
    activity as? LoginActivity?
    }

    View full-size slide

  181. Q. LoginActivityのような具象クラスを扱いたい
    A. castしましょう
    @InstallIn(ActivityComponent::class)
    @Module
    class LoginFeatureModule {
    @Provides
    fun providesLoginActivity(activity: Activity): LoginFeature? =
    (activity as? HasLoginFeature?)?.loginFeature
    }

    View full-size slide

  182. Q. ActivityごとにModuleを切り替えたい
    @Module
    interface SignupActivityModule {
    @ActivityScope
    @ContributesAndroidInjector(modules = [EmailModule::class])
    fun contributesEmailSignupActivity(): EmailSignupActivity
    @ActivityScope
    @ContributesAndroidInjector(modules = [SnsModule::class])
    fun contributesSnsSignupActivity(): SnsSignupActivity
    }

    View full-size slide

  183. Q. ActivityごとにModuleを切り替えたい
    @Module
    interface SignupActivityModule {
    @ActivityScope
    @ContributesAndroidInjector(modules = [EmailModule::class])
    fun contributesEmailSignupActivity(): EmailSignupActivity
    @ActivityScope
    @ContributesAndroidInjector(modules = [SnsModule::class])
    fun contributesSnsSignupActivity(): SnsSignupActivity
    }

    View full-size slide

  184. Q. ActivityごとにModuleを切り替えたい
    @Module
    interface EmailModule {
    @Binds
    fun bindsFeature(feature: EmailSignupFeature): SignupFeature
    }
    @Module
    interface SnsModule {
    @Binds
    fun bindsFeature(feature: SnsSignupFeature): SignupFeature
    }

    View full-size slide

  185. Q. ActivityごとにModuleを切り替えたい
    A. 切り替えたいのはModuleではなくインスタンス
    @InstallIn(ActivityComponent::class)
    @Module
    class SignupModule {
    @Provides
    fun providesFeature(activity: Activity): SignupFeature =
    when (activity) {
    is EmailSignupActivity -> EmailSignupFeature()
    is SnsSignupActivity -> SnsSignupFeature()
    else -> error("Invalid Activity")
    }
    }

    View full-size slide

  186. Q. FCMServiceにinjectするとUIテストが落ちるのですが
    @AndroidEntryPoint
    class MyFirebaseMessagingService : FirebaseMessagingService() {
    @Inject
    lateinit var useCase: RegisterToken
    override fun onNewToken(token: String) {
    super.onNewToken(token)
    useCase.execute(token)
    }
    }

    View full-size slide

  187. Q. FCMServiceにinjectするとUIテストが落ちるのですが
    C Tree
    @Before @After
    C Tree
    @Before @After
    C Tree
    @Before @After

    View full-size slide

  188. Q. FCMServiceにinjectするとUIテストが落ちるのですが
    ContentProvider
    初期化
    C Tree
    @Before @After
    C Tree
    @Before @After
    C Tree
    @Before @After

    View full-size slide

  189. Q. FCMServiceにinjectするとUIテストが落ちるのですが
    A. テストでuseCase実行を諦めましょう
    class MyFirebaseMessagingService : FirebaseMessagingService() {
    @EntryPoint
    @InstallIn(SingletonComponent::class)
    interface LocalEntryPoint {
    fun useCase(): RegisterToken
    }
    }

    View full-size slide

  190. Q. FCMServiceにinjectするとUIテストが落ちるのですが
    A. テストでuseCase実行を諦めましょう
    class MyFirebaseMessagingService : FirebaseMessagingService() {
    private val useCase: RegisterToken? by lazy {
    try {
    EntryPointAccessors.fromApplication(
    applicationContext, LocalEntryPoint::class.java
    ).useCase()
    } catch (t: Throwable) {
    null
    }
    }
    }

    View full-size slide

  191. Q. FCMServiceにinjectするとUIテストが落ちるのですが
    A. テストでuseCase実行を諦めましょう
    class MyFirebaseMessagingService : FirebaseMessagingService() {
    override fun onNewToken(token: String) {
    super.onNewToken(token)
    useCase?.execute(token)
    }
    }

    View full-size slide

  192. Q. 今度はApplicationでUIテストが落ちるのですが
    ContentProvider
    初期化
    C Tree
    @Before @After
    C Tree
    @Before @After
    C Tree
    @Before @After

    View full-size slide

  193. Q. 今度はApplicationでUIテストが落ちるのですが
    App#onCreate
    ContentProvider
    初期化
    C Tree
    @Before @After
    C Tree
    @Before @After
    C Tree
    @Before @After

    View full-size slide

  194. Q. 今度はApplicationでUIテストが落ちるのですが
    A. 初期化処理を遅延実行しましょう

    View full-size slide

  195. open class BaseApp : Application() {
    @EntryPoint
    @InstallIn(SingletonComponent::class)
    interface AppEntryPoint {
    fun appDependencies(): AppDependencies
    }
    private val entryPoint: AppEntryPoint by lazy {
    EntryPointAccessors.fromApplication(this, AppEntryPoint::class.java)
    }
    }

    View full-size slide

  196. open class BaseApp : Application() {
    override fun onCreate() {
    super.onCreate()
    initializeOnCreate()
    }
    protected open fun initializeOnCreate() {
    // ここに必要な初期化処理
    }
    }

    View full-size slide

  197. @CustomTestApplication(AndroidTestApp::class)
    interface HiltAndroidTestApp
    class AndroidTestApp : BaseApp() {
    private val isInitialized = AtomicBoolean(false)
    override fun initializeOnCreate() {
    // 初期化はここでは実施しない
    }
    fun initialize() {
    if (isInitialized.compareAndSet(false, true)) {
    super.initializeOnCreate()
    }
    }
    }

    View full-size slide

  198. @CustomTestApplication(AndroidTestApp::class)
    interface HiltAndroidTestApp
    class AndroidTestApp : BaseApp() {
    private val isInitialized = AtomicBoolean(false)
    override fun initializeOnCreate() {
    // 初期化はここでは実施しない
    }
    fun initialize() {
    if (isInitialized.compareAndSet(false, true)) {
    super.initializeOnCreate()
    }
    }
    }

    View full-size slide

  199. @CustomTestApplication(AndroidTestApp::class)
    interface HiltAndroidTestApp
    class AndroidTestApp : BaseApp() {
    private val isInitialized = AtomicBoolean(false)
    override fun initializeOnCreate() {
    // 初期化はここでは実施しない
    }
    fun initialize() {
    if (isInitialized.compareAndSet(false, true)) {
    super.initializeOnCreate()
    }
    }
    }

    View full-size slide

  200. @CustomTestApplication(AndroidTestApp::class)
    interface HiltAndroidTestApp
    class AndroidTestApp : BaseApp() {
    private val isInitialized = AtomicBoolean(false)
    override fun initializeOnCreate() {
    // 初期化はここでは実施しない
    }
    fun initialize() {
    if (isInitialized.compareAndSet(false, true)) {
    super.initializeOnCreate()
    }
    }
    }

    View full-size slide

  201. class AppInitializeAndroidTestRule : TestRule {
    override fun apply(base: Statement, description: Description): Statement =
    object : Statement() {
    override fun evaluate() {
    val app = ApplicationProvider.getApplicationContext()
    app.initialize()
    base.evaluate()
    }
    }
    }

    View full-size slide

  202. class AppInitializeAndroidTestRule : TestRule {
    override fun apply(base: Statement, description: Description): Statement =
    object : Statement() {
    override fun evaluate() {
    val app = ApplicationProvider.getApplicationContext()
    app.initialize()
    base.evaluate()
    }
    }
    }

    View full-size slide

  203. fun androidTestRule(testInstance: Any): TestRule =
    RuleChain.outerRule(HiltAndroidRule(testInstance))
    .around(AppInitializeAndroidTestRule())
    @HiltAndroidTest
    @RunWith(AndroidJUnit4::class)
    class SomeUiTest {
    @get:Rule
    val rule = androidTestRule(this)
    }

    View full-size slide

  204. Q. 〇〇を□□したい!

    View full-size slide

  205. Q. 〇〇を□□したい!
    A. Component Treeを思い浮かべて、原理から考えよう

    View full-size slide