今日から始める依存性の注入 / First Time Dependency Injection

今日から始める依存性の注入 / First Time Dependency Injection

DroidKaigi 2019
Room1, 2019/02/08 14:50~15:20

85cab5fdf09afe3ee78ce3667681915a?s=128

Keisuke Kobayashi

February 08, 2019
Tweet

Transcript

  1. ࠓ೔͔Β࢝ΊΔґଘੑͷ஫ೖ First Time Dependency Injection Keisuke Kobayashi DroidKaigi 2019 /

    Room1, 2019/02/08 14:50~15:20
  2. ࣗݾ঺հ • Keisuke Kobayashi • Twitter: @kobakei122 • GitHub: @kobakei

    • Merpay, Inc. / Engineering Manager • Studyplus, Inc. / ٕज़ސ໰ʢ෭ۀʣ
  3. ࠓ೔ͷςʔϚ • DIॳ৺ऀɺ·ͨ͸ʮงғؾͰDagger2Λ࢖͍ͬͯΔਓʯʹɺDI ͱ͸Ͳ͏͍͏΋ͷ͔ɺԿͷͨΊʹಋೖ͢Δͷ͔Λղઆ͢Δηο γϣϯͰ͢ • ݸผͷDIίϯςφͷৄ͍͠࢖͍ํʹ͸࣌ؒͷؔ܎্ਂೖΓ͠· ͤΜ

  4. ΞδΣϯμ • Dependency Injection(DI)ͱ͸ʁ • AndroidΞϓϦ։ൃΛྫʹ঺հ • DIίϯςφͷجຊతͳ࢖͍ํ • Dagger2

    & Koin • DIಋೖޙͷςετͷॻ͖ํ
  5. Dependency Injection (DI)ͱ͸ʁ • ίϯϙʔωϯτؒͷґଘؔ܎Λιʔείʔυ͔Βഉআ͠ɺ֎෦ ͔ΒґଘίϯϙʔωϯτΛ஫ೖͤ͞ΔσβΠϯύλʔϯ • ґଘؔ܎Λഉআ͢Δ͜ͱͰɺίϯϙʔωϯτ͕ؒૄ݁߹ʹͳΔ

  6. DIͷϝϦοτ • ίϯϙʔωϯτ͕ؒૄ݁߹ʹͳΔ͜ͱͰɺҎԼͷϝϦοτ͕͋ Δ • ΞϓϦέʔγϣϯΛ֦ு͠΍͘͢ͳΔ • ςετ͕ॻ͖΍͘͢ͳΔ • ͜Ε͸ޙͰৄ͘͠ղઆ

  7. AndroidΞϓϦ։ൃͰ Α͋͘ΔέʔεΛߟ͑Δ

  8. Α͋͘ΔMVPͷྫ HogeActivity HogePresenter UserRepository ApiClient UserDao

  9. Α͋͘ΔMVPͷྫ HogeActivity HogePresenter UserRepository ApiClient UserDao

  10. // DIΛಋೖ͍ͯ͠ͳ͍ίʔυ // ApiClientͱUserDaoʹґଘͨ͠ϦϙδτϦΫϥε class UserRepository { private val apiClient:

    ApiClient = ApiClientImpl() private val userDao: UserDao = UserDaoImpl() fun find(id:Long): Single<User> = return if (userDao.cached) { userDao.find(id) } else { apiClient.getUser(id) } } // UserRepositoryΛ࢖͏ଆ val repo = UserRepository() repo.find(123)...
  11. // DIΛಋೖ͍ͯ͠ͳ͍ίʔυ // ApiClientͱUserDaoʹґଘͨ͠ϦϙδτϦΫϥε class UserRepository { private val apiClient:

    ApiClient = ApiClientImpl() private val userDao: UserDao = UserDaoImpl() fun find(id:Long): Single<User> = return if (userDao.cached) { userDao.find(id) } else { apiClient.getUser(id) } } // UserRepositoryΛ࢖͏ଆ val repo = UserRepository() repo.find(123)... ґଘΦϒδΣΫτͱ
 ີ݁߹͍ͯ͠Δ
  12. // ApiClientͱUserDaoʹґଘͨ͠ϦϙδτϦΫϥε class UserRepository( private val apiClient: ApiClient, private val

    userDao: UserDao ) { fun find(id:Long): Single<User> = return if (userDao.cached) { userDao.find(id) } else { apiClient.getUser(id) } } // UserRepositoryΛ࢖͏ଆ val apiClient = ApiClientImpl() val userDao = UserDaoImpl() val repo = UserRepository(apiClient, userDao) repo.find(123)...
  13. // ApiClientͱUserDaoʹґଘͨ͠ϦϙδτϦΫϥε class UserRepository( private val apiClient: ApiClient, private val

    userDao: UserDao ) { fun find(id:Long): Single<User> = return if (userDao.cached) { userDao.find(id) } else { apiClient.getUser(id) } } // UserRepositoryΛ࢖͏ଆ val apiClient = ApiClientImpl() val userDao = UserDaoImpl() val repo = UserRepository(apiClient, userDao) repo.find(123)... ίϯετϥΫλͰ
 ґଘΦϒδΣΫτΛ ड͚औΔ
  14. // ApiClientͱUserDaoʹґଘͨ͠ϦϙδτϦΫϥε class UserRepository( private val apiClient: ApiClient, private val

    userDao: UserDao ) { fun find(id:Long): Single<User> = return if (userDao.cached) { userDao.find(id) } else { apiClient.getUser(id) } } // UserRepositoryΛ࢖͏ଆ val apiClient = ApiClientImpl() val userDao = UserDaoImpl() val repo = UserRepository(apiClient, userDao) repo.find(123)... ࢖͏ଆ͕ґଘΦϒδΣΫτ͔Β
 ૊Έཱ͍ͯͯΔ
  15. // ґଘΦϒδΣΫτΛղܾ͢ΔͨΊͷίϯςφΦϒδΣΫτ object Container { val apiClient: ApiClient = ApiClientImpl()

    val userDao: UserDao = UserDaoImpl() val userRepo: UserRepository = UserRepository(apiClient, userDao) }
  16. // ApiClientͱUserDaoʹґଘͨ͠ϦϙδτϦΫϥε class UserRepository( private val apiClient: ApiClient, private val

    userDao: UserDao ) { fun find(id: Long): Single<User> = return if (userDao.cached) { userDao.find(id) } else { apiClient.getUser(id) } } // UserRepositoryΛ࢖͏ଆ val repo = Container.userRepo repo.find(123)... UserRepositoryͷґଘؔ܎ ͕ഉআ͞Εͨ
  17. Α͋͘ΔMVPͷྫ HogeActivity HogePresenter UserRepository ApiClient UserDao

  18. Activityͷ৔߹ • ίϯετϥΫλͰͷ஫ೖ͕Ͱ͖ͳ͍ • Fragment, Service, BroadcastReceiverͳͲ΋ಉ͡

  19. // DIΛಋೖ͍ͯ͠ͳ͍ίʔυ class HogeActivity : AppCompatActivity() { private val presenter

    = HogePresenter() override fun onCreate(savedInstanceState: Bundle?) { // লུ } fun onButtonClick(view: View) { presenter.onButtonClick() } } Activity͕ HogePresenterʹ
 ີ݁߹͍ͯ͠Δ
  20. // DIΛಋೖ͍ͯ͠ͳ͍ίʔυ class HogeActivity : AppCompatActivity() { private lateinit var

    presenter: HogePresenter override fun onCreate(savedInstanceState: Bundle?) { // লུ presenter = HogePresenter(applicationContext) } fun onButtonClick(view: View) { presenter.onButtonClick() } } ͜Ε΋ີ݁߹͍ͯ͠Δ
  21. // ґଘΦϒδΣΫτΛղܾ͢ΔͨΊͷίϯςφΦϒδΣΫτ object Container { val apiClient: ApiClient = ApiClientImpl()

    val userDao: UserDao = UserDaoImpl() val userRepo: UserRepository = UserRepository(apiClient, userDao) private var hogePresenter: HogePresenter? = null fun resolveHogePresenter(context: Context): HogePresenter { if (hogePresenter == null) { hogePresenter = HogePresenter(context, userRepo) } return requireNotNull(hogePresenter) } } ContextΛҾ਺ʹऔΔϝιουΛ௥Ճ
  22. // DIΛಋೖޙͷίʔυ class HogeActivity : AppCompatActivity() { private val presenter:

    HogePresenter by lazy { Container.resolveHogePresenter(applicationContext) } override fun onCreate(savedInstanceState: Bundle?) { // লུ }ɹ fun onButtonClick(view: View) { presenter.onButtonClick() } } by lazyΛ࢖͏͜ͱͰɺ Application ContextΛ౉ͤΔ
  23. ͜͜ͰؾʹͳΔ͜ͱ͕

  24. PresenterͷϥΠϑαΠΫϧ͜ΕͰେৎ෉ʁ • Presenter͸Activityͱಉ͡ϥΠϑαΠΫϧʹ͍ͨ͠ • Activity͕ੜ੒͞ΕΔͱPresenter΋ੜ੒͞ΕɺActivity͕ഁغ ͞ΕΔͱಉ࣌ʹഁغ͞ΕΔ΂͖ • ActivityͷΠϯελϯε͕2ͭ͋Δͱ͖͸ɺͦΕͧΕʹ Presenter͕͍ͯ΄͍͠

  25. είʔϓ • ґଘΦϒδΣΫτͷੜଘظؒΛද͢ • SingletonɺActivityͱಉ͡ੜଘظؒɺͳͲ • είʔϓ͝ͱʹґଘΦϒδΣΫτ͕ੜ੒͞Εɺ࢖͍ճ͞ΕΔΑ ͏ͳ࢓૊Έ͕ඞཁ

  26. HogeActivity HogePresenter UserRepository ApiClient UserDao FugaActivity FugaPresenter

  27. HogeActivity HogePresenter UserRepository ApiClient UserDao FugaActivity FugaPresenter ը໘ڞ௨Ͱ࢖͍·Θ͍ͨ͠ ֤ը໘͝ͱʹੜ੒͍ͨ͠

  28. HogeActivity HogePresenter UserRepository ApiClient UserDao FugaActivity FugaPresenter Singleton Singleton Singleton

    ActivityScope ActivityScope
  29. object Container { // লུ val userRepo = UserRepository(apiClient, logger)

    } class HogeActivityScopeContainer { fun resolveHogePresenter(ctx: Context): HogePresenter {…} } class FugaActivityScopeContainer { fun resolveFugaPresenter(ctx: Context): FugaPresenter {…} }
  30. object Container { // লུ val userRepo = UserRepository(apiClient, logger)

    } class HogeActivityScopeContainer { fun resolveHogePresenter(ctx: Context): HogePresenter {…} } class FugaActivityScopeContainer { fun resolveFugaPresenter(ctx: Context): FugaPresenter {…} } Singletonͳ ΫϥεͷΈ
  31. object Container { // লུ val userRepo = UserRepository(apiClient, logger)

    } class HogeActivityScopeContainer { fun resolveHogePresenter(ctx: Context): HogePresenter {…} } class FugaActivityScopeContainer { fun resolveFugaPresenter(ctx: Context): FugaPresenter {…} } HogeActivityͱಉ͡
 είʔϓͷΫϥεΛฦ͢
  32. // DIΛಋೖޙͷίʔυ class HogeActivity : AppCompatActivity() { private val container

    = HogeActivityScopeContainer() private val presenter: HogePresenter by lazy { container.resolveHogePresenter(applicationContext) } override fun onCreate(savedInstanceState: Bundle?) { // লུ }ɹ fun onButtonClick(view: View) { presenter.onButtonClick() } } ͜ͷActivityͷ ίϯςφΛॳظԽ
  33. // DIΛಋೖޙͷίʔυ class HogeActivity : AppCompatActivity() { private val container

    = HogeActivityScopeContainer() private val presenter: HogePresenter by lazy { container.resolveHogePresenter(applicationContext) } override fun onCreate(savedInstanceState: Bundle?) { // লུ }ɹ fun onButtonClick(view: View) { presenter.onButtonClick() } } containerΛ࢖ͬͯ஫ೖ
  34. ͜͜·Ͱͷ·ͱΊ • DIͱ͸ίϯϙʔωϯτؒͷґଘؔ܎Λ֎෦ʹ௥͍ग़͢ύλʔϯ • ґଘίϯϙʔωϯτΛ֎෦͔Βड͚औΔΑ͏ʹ͢Δ • είʔϓ͸ΦϒδΣΫτͷੜଘظؒΛද͠ɺείʔϓ͝ͱʹίϯςφ Λ࣋ͭ • ͜͜·Ͱͷ࣮૷͸͋͘·Ͱղઆ༻ͷࡶͳ΋ͷͳͷͰ஫ҙʂ


    ΑΓ࣮ફతͳ࣮૷ํ๏͸࣍Ҏ߱
  35. DIίϯςφ

  36. DIίϯςφ • DIΛ࣮ݱ͢ΔͨΊͷϥΠϒϥϦ • ઌఔࣗ෼Ͱ࣮૷ͨ͠ContainerʢΑΓ΋ͬͱ͍͍΋ͷʣΛࣗಈ Ͱ࡞ͬͯ͘ΕΔϥΠϒϥϦ

  37. AndroidͰ༗໊ͳDIίϯςφ • Dagger2 • Koin • Kodein • Toothpick •

    Roboguice
  38. AndroidͰ༗໊ͳDIίϯςφ • Dagger2 • Koin • Kodein • Toothpick •

    Roboguice
  39. Dagger2 https://google.github.io/dagger/

  40. Dagger2 • ݩʑSquare͕։ൃ => ݱࡏ͸Google͕fork • Annotation processorΛ࢖༻ • ίϯύΠϧ࣌ʹґଘπϦʔʹ໰୊͕͋Δͱ෼͔Δ"

    • ίϯετϥΫλ͕࢖͑ΔΫϥεͷ৔߹ɺґଘؔ܎ͷղܾΛࣗಈੜ੒Ͱ͖Δ" • Ϗϧυ͸஗͘ͳΔ# • υΩϡϝϯτ͕೉͍͠###
  41. ஫ҙʂ • Dagger2͸͔ͳΓଟػೳͰ͕͢ɺ࣌ؒͷؔ܎্͋ͬ͞Γ͔͠঺հͰ͖· ͤΜ • Dagger2ʹ͸AndroidʹݶΒͳ͍௨ৗ൛ͱAndroidαϙʔτ൛ ʢdagger.androidʣ͕͋Δ • ࠓ೔͸dagger.androidͰͷActivity΁ͷ஫ೖΛ঺հ •

    ʮ͓·͡ͳ͍ʯ͕େྔʹग़ͯ͘ΔͷͰ஫ҙ
  42. Dagger2ͷొ৔ਓ෺ • Module • Component

  43. Module • ֤ґଘΫϥεΛͲ͏ΠϯελϯεԽ͢Δ͔Λఆٛ͢ΔΫϥε • Daggerͷ৔߹ίϯετϥΫλΛ࢖ͬͯΠϯελϯεԽ͢Δ
 Ϋϥε͸ࣗಈతʹੜ੒Ͱ͖Δɻ
 ίϯετϥΫλ͕࢖͑ͳ͍ΫϥεΛͲ͏΍ͬͯΠϯελϯεԽ ͢Δ͔͚ͩఆٛ͢Ε͹Α͍ɻ

  44. Α͋͘ΔMVPͷྫ HogeActivity HogePresenter UserRepository ApiClient UserDao

  45. class ApiClientImpl private constructor(): ApiClient { class Builder { fun

    build(): ApiClient {...} } override fun getUser(id: Long): Single<User> {...} } class UserDaoImpl(): UserDao { override fun find(id: Long): Single<User> {...} } class UserRepository( private val apiClient: ApiClient, private val userDao: UserDao ) { fun find(id: Long): Single<Hoge> {...} }
  46. class ApiClientImpl private constructor(): ApiClient { class Builder { fun

    build(): ApiClient {...} } override fun getUser(id: Long): Single<User> {...} } class UserDaoImpl(): UserDao { override fun find(id: Long): Single<User> {...} } class UserRepository( private val apiClient: ApiClient, private val userDao: UserDao ) { fun find(id: Long): Single<Hoge> {...} } ࣗಈੜ੒Ͱ͖Δ"
 =>Moduleʹॻ͘ඞཁͳ͠ ࣗಈੜ੒Ͱ͖ͳ͍#
 => Moduleʹॻ͘ඞཁ͋Γ
  47. @Module class AppModule { @Singleton @Provides fun provideApiClient(): ApiClient {

    return ApiClientImpl.Builder().build() } }
  48. @Module class AppModule { @Singleton @Provides fun provideApiClient(): ApiClient {

    return ApiClientImpl.Builder().build() } } Dagger2ͷϞδϡʔϧΛҙຯ͢Δ
  49. @Module class AppModule { @Singleton @Provides fun provideApiClient(): ApiClient {

    return ApiClientImpl.Builder().build() } }
  50. @Module class AppModule { @Singleton @Provides fun provideApiClient(): ApiClient {

    return ApiClientImpl.Builder().build() } } ͜ͷϝιουΛґଘղܾʹ࢖͏
  51. @Module class AppModule { @Singleton @Provides fun provideApiClient(): ApiClient {

    return ApiClientImpl.Builder().build() } } ApiClientͷείʔϓ
  52. class ApiClientImpl private constructor(): ApiClient { class Builder { fun

    build(): ApiClient {...} } override fun getUser(id: Long): Single<User> {...} } class UserDaoImpl(): UserDao { override fun find(id: Long): Single<User> {...} } class UserRepository( private val apiClient: ApiClient, private val userDao: UserDao ) { fun find(id: Long): Single<Hoge> {...} } ࣗಈੜ੒Ͱ͖Δ"
 =>Moduleʹॻ͘ඞཁͳ͠ ࣗಈੜ੒Ͱ͖ͳ͍#
 => Moduleʹॻ͘ඞཁ͋Γ
  53. @Singleton class UserRepository @Inject constructor( private val apiClient: ApiClient, private

    val userDao: UserDao ) { fun find(id: Long): Single<User> {...} } ͜ͷίϯετϥΫλΛ࢖ͬͯΠϯελϯεԽ͞ΕΔ
  54. @Module abstract class ActivityModule { @ActivityScope @ContributesAndroidInjector abstract fun contributeHogeActivity():

    HogeActivity } dagger.androidΛ࢖͏ͨΊͷಛघͳϞδϡʔϧ @ProvidesϝιουΛ࣋ͨͳ͍
  55. @Module abstract class ActivityModule { @ActivityScope @ContributesAndroidInjector abstract fun contributeHogeActivity():

    HogeActivity } HogeActivityʹ஫ೖ͢ΔͨΊͷϝιου
  56. Component • ModuleΛ࢖ͬͯґଘ͍ͯ͠ΔΦϒδΣΫτΛੜ੒ɾ஫ೖ͢Δ
 Ϋϥε

  57. @Singleton @Component(modules = [ AppModule::class, ActivityModule::class, AndroidInjectionModule::class ]) interface AppComponent:

    AndroidInjector<App> { @Component.Builder abstract class Builder: AndroidInjector.Builder<App>() }
  58. @Singleton @Component(modules = [ AppModule::class, ActivityModule::class, AndroidInjectionModule::class ]) interface AppComponent:

    AndroidInjector<App> { @Component.Builder abstract class Builder: AndroidInjector.Builder<App>() } ͜ͷίϯϙʔωϯτ͕࢖͏ Ϟδϡʔϧͷ഑ྻ
  59. @Singleton @Component(modules = [ AppModule::class, ActivityModule::class, AndroidInjectionModule::class ]) interface AppComponent:

    AndroidInjector<App> { @Component.Builder abstract class Builder: AndroidInjector.Builder<App>() } Android support༻ͷΠϯλϑΣʔεΛܧঝͤ͞Δ
  60. class App : Application(), HasActivityInjector { @Inject lateinit var injector:

    DispatchingAndroidInjector<Activity> override fun onCreate() { super.onCreate() // Dagger2ͷॳظԽ DaggerAppComponent.builder() .create(this) .inject(this) } override fun activityInjector(): AndroidInjector<Activity> = injector }
  61. class App : Application(), HasActivityInjector { @Inject lateinit var injector:

    DispatchingAndroidInjector<Activity> override fun onCreate() { super.onCreate() // Dagger2ͷॳظԽ DaggerAppComponent.builder() .create(this) .inject(this) } override fun activityInjector(): AndroidInjector<Activity> = injector } Android support༻ͷΠϯλϑΣʔεΛܧঝͤ͞Δ
  62. class App : Application(), HasActivityInjector { @Inject lateinit var injector:

    DispatchingAndroidInjector<Activity> override fun onCreate() { super.onCreate() // Dagger2ͷॳظԽ DaggerAppComponent.builder() .create(this) .inject(this) } override fun activityInjector(): AndroidInjector<Activity> = injector } AppʹinjectorΛ஫ೖ
  63. class HogeActivity : AppCompatActivity() { @Inject lateinit var presenter: HogePresenter

    override fun onCreate(savedInstanceState: Bundle?) { // লུ AndroidInjection.inject(this) } fun onButtonClick(view: View) { presenter.onButtonClick() } }
  64. class HogeActivity : AppCompatActivity() { @Inject lateinit var presenter: HogePresenter

    override fun onCreate(savedInstanceState: Bundle?) { // লུ AndroidInjection.inject(this) } fun onButtonClick(view: View) { presenter.onButtonClick() } } ґଘΦϒδΣΫτʹ͸ @InjectΛ෇͚͓ͯ͘
  65. class HogeActivity : AppCompatActivity() { @Inject lateinit var presenter: HogePresenter

    override fun onCreate(savedInstanceState: Bundle?) { // লུ AndroidInjection.inject(this) } fun onButtonClick(view: View) { presenter.onButtonClick() } } ґଘΦϒδΣΫτΛ஫ೖ
  66. ͳΔ΄ͲɺΘ͔ΒΜ • େৎ෉ • ଟ෼ΈΜͳ࠷ॳ͸෼͔ͬͯͳ͍ • ࠓޙDaggerΛಋೖ͢Δͱ͖ʹɺ΋͏Ұ౓͜ͷࢿྉ΍ଞͷαϯϓ ϧΛݟ௚ͤ͹OK

  67. Koin https://insert-koin.io/

  68. Koin • KotlinͰॻ͔ΕͨDIίϯςφ • ҠৡϓϩύςΟΛ࢖༻ͯ͠ґଘੑΛղܾ • Annotation processorෆ࢖༻ • Ϗϧυ଎౓ʹѱӨڹ͸ͳ͍"

    • ࣮ߦ͢Δ·ͰґଘπϦʔʹ໰୊͕ͳ͍͔෼͔Βͳ͍# • constructor injectionͰ͖ΔΫϥεͰ΋ɺϞδϡʔϧʹॻ͘ඞཁ͋Γ# • υΩϡϝϯτ͕Θ͔Γ΍͍͢" • AAC ViewModelʹରԠ"
  69. ࢖͍ํ 1. moduleͷఆٛ • ͜͜͸Dagger2ͱಉ͡ 2. startKoinͰίϯςφ࡞੒ 3. ஫ೖઌͷΫϥεͰҠৡϓϩύςΟΛ࢖ͬͯ஫ೖ͢Δ

  70. class App : Application() { override fun onCreate() { super.onCreate()

    // KoinͷϞδϡʔϧͷઃఆ val appModule = module { single<Logger> { LoggerImpl() } factory<ApiClient> { ApiClientImpl.Builder().build() } factory<HogeRepository> { HogeRepository(get()) } } // ґଘπϦʔͷ࡞੒ startKoin(this, listOf(appModule)) } }
  71. class App : Application() { override fun onCreate() { super.onCreate()

    // KoinͷϞδϡʔϧͷઃఆ val appModule = module { single<Logger> { LoggerImpl() } factory<ApiClient> { ApiClientImpl.Builder().build() } factory<HogeRepository> { HogeRepository(get()) } } // ґଘπϦʔͷ࡞੒ startKoin(this, listOf(appModule)) } } Ϟδϡʔϧͷ࡞੒
  72. class App : Application() { override fun onCreate() { super.onCreate()

    // KoinͷϞδϡʔϧͷઃఆ val appModule = module { single<Logger> { LoggerImpl() } factory<ApiClient> { ApiClientImpl.Builder().build() } factory<HogeRepository> { HogeRepository(get()) } } // ґଘπϦʔͷ࡞੒ startKoin(this, listOf(appModule)) } } Singleton
  73. class App : Application() { override fun onCreate() { super.onCreate()

    // KoinͷϞδϡʔϧͷઃఆ val appModule = module { single<Logger> { LoggerImpl() } factory<ApiClient> { ApiClientImpl.Builder().build() } factory<HogeRepository> { HogeRepository(get()) } } // ґଘπϦʔͷ࡞੒ startKoin(this, listOf(appModule)) } } ΦϒδΣΫτΛ ຖճ࡞੒͢ΔΫϥε
  74. class App : Application() { override fun onCreate() { super.onCreate()

    // KoinͷϞδϡʔϧͷઃఆ val appModule = module { single<Logger> { LoggerImpl() } factory<ApiClient> { ApiClientImpl.Builder().build() } factory<HogeRepository> { HogeRepository(get()) } } // ґଘπϦʔͷ࡞੒ startKoin(this, listOf(appModule)) } } getͰґଘղܾ
  75. class App : Application() { override fun onCreate() { super.onCreate()

    // KoinͷϞδϡʔϧͷઃఆ val appModule = module { single<Logger> { LoggerImpl() } factory<ApiClient> { ApiClientImpl.Builder().build() } factory<HogeRepository> { HogeRepository(get()) } } // ґଘπϦʔͷ࡞੒ startKoin(this, listOf(appModule)) } } ίϯςφͷ࡞੒
  76. class HogeActivity : AppCompatActivity() { val logger: Logger by inject()

    override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // ... logger.v(“Hello world!”) } } ҕৡϓϩύςΟΛ࢖ͬͯ஫ೖ
  77. AAC ViewModelʹରԠ • ViewModel͸Factoryܦ༝ͰΠϯελϯεΛੜ੒͢Δ • FactoryΛܧঝ͢Δ࢓૊Έ͕͋ΓɺDIͱ૊Έ߹ΘͤΔͷ͸
 τϦοΫ͕ඞཁ • Koin͸ରԠ͍ͯ͠Δʂ

  78. class HogeViewModel( private val userRepository: UserRepository ) : ViewModel() {…}

  79. class App : Application() { override fun onCreate() { super.onCreate()

    // KoinͷϞδϡʔϧͷઃఆ val appModule = module { // ... viewModel { HogeViewModel(get()) } } startKoin(this, listOf(appModule)) } } ViewModelͷΠϯελϯεԽ
  80. class HogeActivity : AppCompatActivity() { val hogeViewModel: HogeViewModel by viewModel()

    override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // ... } fun onButtonClick(view: View) { hogeViewModel.onButtonClick() } } ViewModel༻ͷ ҕৡϓϩύςΟΛ࢖͏
  81. ͜͜·Ͱͷ·ͱΊ • DIίϯςφ = DIΛ؆୯ʹ࣮૷͢ΔͨΊͷϥΠϒϥϦ • Dagger2…ଟػೳ͕ͩ೉͍͠ɻconstructor injectionͷ৔߹͸ ϞδϡʔϧলུՄೳɻ •

    Koin…Θ͔Γ΍͍͢ɻAAC ViewModelʹରԠ͍ͯ͠Δɻ
  82. DIΛಋೖͯ͠Կ͕มΘͬͨʁ

  83. DIͷϝϦοτʢ࠶ܝʣ • ίϯϙʔωϯτ͕ؒૄ݁߹ʹͳΔ͜ͱͰɺҎԼͷϝϦοτ͕͋ Δ • ΞϓϦέʔγϣϯΛ֦ு͠΍͘͢ͳΔ • ςετ͕ॻ͖΍͘͢ͳΔ • ͜Ε͸ޙͰৄ͘͠ղઆ

  84. ୯ମςετ

  85. // ςετର৅ͷΫϥεʢDIಋೖલʣ class UserRepository { private val apiClient: ApiClient =

    ApiClientImpl() fun find(id: Long): Single<User> = apiClient.getUser(id) } ApiClientʹີ݁߹͍ͯ͠Δ
  86. // UserRepositoryͷςετίʔυ class UserRepositoryTest { @Test fun find_isSuccess() { val

    repo = UserRepository() repo.find(1) .test() .awaitCount(1) .assertValue(expected) } }
  87. // UserRepositoryͷςετίʔυ class UserRepositoryTest { @Test fun find_isSuccess() { val

    repo = UserRepository() repo.find(1) .test() .awaitCount(1) .assertValue(expected) } } ApiClientImplͷ
 ίʔυ΋࣮ߦ͍ͯ͠Δ
  88. HogeActivity HogeViewModel UserRepository ApiClient ͕͜͜ςετର৅ʹ ͳͬͯ͠·͍ͬͯΔ

  89. HogeActivity HogeViewModel UserRepository ApiClient ຊ౰ʹςετ͍ͨ͠ͷ͸ ͚ͩ͜͜

  90. DIΛಋೖ͍ͯ͠Δ৔߹ • ґଘίϯϙʔωϯτΛ஫ೖ͢Δͱ͖ʹɺςετʹ౎߹ͷ͍͍
 ΦϒδΣΫτΛ࢖͏͜ͱͰɺର৅ͷΫϥεͷΈݕূՄೳ • Mockk, MockitoͳͲͰϞοΫʹ͢Δ͜ͱ͕ଟ͍

  91. // ςετର৅ͷΫϥεʢDIಋೖޙʣ class UserRepository( private val apiClient: ApiClient ) {

    fun find(id: Long): Single<User> = apiClient.getUser(id) } ApiClientΛ ֎෦͔Β஫ೖՄೳ
  92. // UserRepositoryͷςετίʔυ class UserRepositoryTest { @Test fun find_isSuccess() { val

    apiClient = mockk<ApiClient> { every { getUser(any()) } returns Single.just(user) } val repo = UserRepository(apiClient) repo.find(1) .test() .awaitCount(1) .assertValue(user) } }
  93. // UserRepositoryͷςετίʔυ class UserRepositoryTest { @Test fun find_isSuccess() { val

    apiClient = mockk<ApiClient> { every { getUser(any()) } returns Single.just(user) } val repo = UserRepository(apiClient) repo.find(1) .test() .awaitCount(1) .assertValue(user) } } ApiClientͷϞοΫΛ࡞੒
  94. // UserRepositoryͷςετίʔυ class UserRepositoryTest { @Test fun find_isSuccess() { val

    apiClient = mockk<ApiClient> { every { getUser(any()) } returns Single.just(user) } val repo = UserRepository(apiClient) repo.find(1) .test() .awaitCount(1) .assertValue(user) } } ϞοΫΛ஫ೖͯ͠ ςετ࣮ߦ
  95. ݁߹ςετ

  96. ݁߹ςετ • ???ʮͲ͏ͤ݁߹ͨ͠ঢ়ଶͰςετ͢Δ͔ΒDIͰίϯϙʔωϯ τؒΛૄ݁߹ʹͯ͠΋ҙຯͳ͍Μ͡Όͳ͍Μͷʁʯ • ͦΜͳ͜ͱ͸ͳ͍

  97. ྫ: EspressoͰUIςετΛॻ͘ • APIΫϥΠΞϯτͷԠ౴͕ຖճҧ͏ͷͰਏ͍ • Ϩεϙϯε͕࣌ؒʹΑͬͯҧ͏ • Ԡ౴࣌ؒ • Α͘API͕յΕΔʢ։ൃ؀ڥͳͲʣ

    • DIΛಋೖ͍ͯ͠Δͱɺ؆୯ʹAPIΫϥΠΞϯτΛςετ༻ͷϞοΫʹࠩ͠ସ͑ Δ͜ͱ͕Ͱ͖Δ
  98. DIίϯςφͷϞδϡʔϧΛςετ༻ʹ
 ࠩ͠ସ͑Δ 1. ςετ༻ͷApplicationΛ࡞੒͠ɺ಺෦Ͱςετ༻Ϟδϡʔϧ Λ࢖ͬͯґଘπϦʔΛ࡞Δ 2. Instrumented test࣮ߦ࣌ʹىಈ͢ΔApplicationΫϥεΛɺ
 1Ͱ࡞ͬͨΫϥεʹࠩ͠ସ͑Δ

  99. open class App : Application() { private val appModule =

    module { single<Logger> { LoggerImpl() } factory { UserRepository(get()) } viewModel { MainViewModel(get(), get()) } } // APIΫϥΠΞϯτ༻Ϟδϡʔϧ protected open val apiModule = module { factory<ApiClient> { ApiClientImpl() } } override fun onCreate() { super.onCreate() // ґଘπϦʔΛߏங startKoin(this, listOf(appModule, apiModule)) } }
  100. open class App : Application() { private val appModule =

    module { single<Logger> { LoggerImpl() } factory { UserRepository(get()) } viewModel { MainViewModel(get(), get()) } } // APIΫϥΠΞϯτ༻Ϟδϡʔϧ protected open val apiModule = module { factory<ApiClient> { ApiClientImpl() } } override fun onCreate() { super.onCreate() // ґଘπϦʔΛߏங startKoin(this, listOf(appModule, apiModule)) } } ςετ༻ͷAppΫϥεΛ
 ܧঝͯ͠࡞ΕΔΑ͏ʹ͢Δ
  101. open class App : Application() { private val appModule =

    module { single<Logger> { LoggerImpl() } factory { UserRepository(get()) } viewModel { MainViewModel(get(), get()) } } // APIΫϥΠΞϯτ༻Ϟδϡʔϧ protected open val apiModule = module { factory<ApiClient> { ApiClientImpl() } } override fun onCreate() { super.onCreate() // ґଘπϦʔΛߏங startKoin(this, listOf(appModule, apiModule)) } } ͜͜΋ΦʔόʔϥΠυ Ͱ͖ΔΑ͏ʹ͢Δ
  102. class TestApp : App() { override val apiModule = module

    { factory<ApiClient> { val user = User(123, “kobakei”) val apiClient = mockk<ApiClient> { every { getUser(any()) } returns Single.just(user) } apiClient } } }
  103. class TestApp : App() { override val apiModule = module

    { factory<ApiClient> { val user = User(123, “kobakei”) val apiClient = mockk<ApiClient> { every { getUser(any()) } returns Single.just(user) } apiClient } } } ϞοΫͷAPIΫϥΠΞϯτΛฦ͢ ϞδϡʔϧͰΦʔόʔϥΠυ
  104. // ΞϓϦέʔγϣϯΛࠩ͠ସ͑ΔͨΊͷϥϯφʔ class MyTestRunner : AndroidJUnitRunner() { override fun newApplication(cl:

    ClassLoader?, name: String?, c: Context?) = super.newApplication(cl, TestApp::class.java.name, c) } TestAppΫϥεΛىಈ͢Δ
  105. // app/build.gradle android { defaultConfig { testInstrumentationRunner “your.app.MyTestRunner” } }

    Instrumented test࣌ͷϥϯφʔΛࢦఆ
  106. ຊ೔ͷ·ͱΊ

  107. ·ͱΊ • DI = ίϯϙʔωϯτؒͷґଘؔ܎Λ֎෦ʹ௥͍ग़͢σβΠϯύ λʔϯ • DIΛಋೖ͢Δ͜ͱͰɺίϯϙʔωϯτ͕ૄ݁߹ʹͳΓɺίϯ ϙʔωϯτΛࠩ͠ସ͑ͨΓɺςετ͕ॻ͖΍͘͢ͳΔ •

    Dagger2΍KoinͳͲDIίϯςφΛར༻͠Α͏
  108. Thanks!!!