Slide 1

Slide 1 text

ࠓ೔͔Β࢝ΊΔґଘੑͷ஫ೖ First Time Dependency Injection Keisuke Kobayashi DroidKaigi 2019 / Room1, 2019/02/08 14:50~15:20

Slide 2

Slide 2 text

ࣗݾ঺հ • Keisuke Kobayashi • Twitter: @kobakei122 • GitHub: @kobakei • Merpay, Inc. / Engineering Manager • Studyplus, Inc. / ٕज़ސ໰ʢ෭ۀʣ

Slide 3

Slide 3 text

ࠓ೔ͷςʔϚ • DIॳ৺ऀɺ·ͨ͸ʮงғؾͰDagger2Λ࢖͍ͬͯΔਓʯʹɺDI ͱ͸Ͳ͏͍͏΋ͷ͔ɺԿͷͨΊʹಋೖ͢Δͷ͔Λղઆ͢Δηο γϣϯͰ͢ • ݸผͷDIίϯςφͷৄ͍͠࢖͍ํʹ͸࣌ؒͷؔ܎্ਂೖΓ͠· ͤΜ

Slide 4

Slide 4 text

ΞδΣϯμ • Dependency Injection(DI)ͱ͸ʁ • AndroidΞϓϦ։ൃΛྫʹ঺հ • DIίϯςφͷجຊతͳ࢖͍ํ • Dagger2 & Koin • DIಋೖޙͷςετͷॻ͖ํ

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

// ApiClientͱUserDaoʹґଘͨ͠ϦϙδτϦΫϥε class UserRepository( private val apiClient: ApiClient, private val userDao: UserDao ) { fun find(id:Long): Single = 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)...

Slide 13

Slide 13 text

// ApiClientͱUserDaoʹґଘͨ͠ϦϙδτϦΫϥε class UserRepository( private val apiClient: ApiClient, private val userDao: UserDao ) { fun find(id:Long): Single = 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)... ίϯετϥΫλͰ
 ґଘΦϒδΣΫτΛ ड͚औΔ

Slide 14

Slide 14 text

// ApiClientͱUserDaoʹґଘͨ͠ϦϙδτϦΫϥε class UserRepository( private val apiClient: ApiClient, private val userDao: UserDao ) { fun find(id:Long): Single = 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)... ࢖͏ଆ͕ґଘΦϒδΣΫτ͔Β
 ૊Έཱ͍ͯͯΔ

Slide 15

Slide 15 text

// ґଘΦϒδΣΫτΛղܾ͢ΔͨΊͷίϯςφΦϒδΣΫτ object Container { val apiClient: ApiClient = ApiClientImpl() val userDao: UserDao = UserDaoImpl() val userRepo: UserRepository = UserRepository(apiClient, userDao) }

Slide 16

Slide 16 text

// ApiClientͱUserDaoʹґଘͨ͠ϦϙδτϦΫϥε class UserRepository( private val apiClient: ApiClient, private val userDao: UserDao ) { fun find(id: Long): Single = return if (userDao.cached) { userDao.find(id) } else { apiClient.getUser(id) } } // UserRepositoryΛ࢖͏ଆ val repo = Container.userRepo repo.find(123)... UserRepositoryͷґଘؔ܎ ͕ഉআ͞Εͨ

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

// ґଘΦϒδΣΫτΛղܾ͢ΔͨΊͷίϯςφΦϒδΣΫτ 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ΛҾ਺ʹऔΔϝιουΛ௥Ճ

Slide 22

Slide 22 text

// 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Λ౉ͤΔ

Slide 23

Slide 23 text

͜͜ͰؾʹͳΔ͜ͱ͕

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

HogeActivity HogePresenter UserRepository ApiClient UserDao FugaActivity FugaPresenter

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

HogeActivity HogePresenter UserRepository ApiClient UserDao FugaActivity FugaPresenter Singleton Singleton Singleton ActivityScope ActivityScope

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

object Container { // লུ val userRepo = UserRepository(apiClient, logger) } class HogeActivityScopeContainer { fun resolveHogePresenter(ctx: Context): HogePresenter {…} } class FugaActivityScopeContainer { fun resolveFugaPresenter(ctx: Context): FugaPresenter {…} } HogeActivityͱಉ͡
 είʔϓͷΫϥεΛฦ͢

Slide 32

Slide 32 text

// 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ͷ ίϯςφΛॳظԽ

Slide 33

Slide 33 text

// 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Λ࢖ͬͯ஫ೖ

Slide 34

Slide 34 text

͜͜·Ͱͷ·ͱΊ • DIͱ͸ίϯϙʔωϯτؒͷґଘؔ܎Λ֎෦ʹ௥͍ग़͢ύλʔϯ • ґଘίϯϙʔωϯτΛ֎෦͔Βड͚औΔΑ͏ʹ͢Δ • είʔϓ͸ΦϒδΣΫτͷੜଘظؒΛද͠ɺείʔϓ͝ͱʹίϯςφ Λ࣋ͭ • ͜͜·Ͱͷ࣮૷͸͋͘·Ͱղઆ༻ͷࡶͳ΋ͷͳͷͰ஫ҙʂ
 ΑΓ࣮ફతͳ࣮૷ํ๏͸࣍Ҏ߱

Slide 35

Slide 35 text

DIίϯςφ

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

Dagger2 • ݩʑSquare͕։ൃ => ݱࡏ͸Google͕fork • Annotation processorΛ࢖༻ • ίϯύΠϧ࣌ʹґଘπϦʔʹ໰୊͕͋Δͱ෼͔Δ" • ίϯετϥΫλ͕࢖͑ΔΫϥεͷ৔߹ɺґଘؔ܎ͷղܾΛࣗಈੜ੒Ͱ͖Δ" • Ϗϧυ͸஗͘ͳΔ# • υΩϡϝϯτ͕೉͍͠###

Slide 41

Slide 41 text

஫ҙʂ • Dagger2͸͔ͳΓଟػೳͰ͕͢ɺ࣌ؒͷؔ܎্͋ͬ͞Γ͔͠঺հͰ͖· ͤΜ • Dagger2ʹ͸AndroidʹݶΒͳ͍௨ৗ൛ͱAndroidαϙʔτ൛ ʢdagger.androidʣ͕͋Δ • ࠓ೔͸dagger.androidͰͷActivity΁ͷ஫ೖΛ঺հ • ʮ͓·͡ͳ͍ʯ͕େྔʹग़ͯ͘ΔͷͰ஫ҙ

Slide 42

Slide 42 text

Dagger2ͷొ৔ਓ෺ • Module • Component

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

@Module class AppModule { @Singleton @Provides fun provideApiClient(): ApiClient { return ApiClientImpl.Builder().build() } } ApiClientͷείʔϓ

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

@Singleton class UserRepository @Inject constructor( private val apiClient: ApiClient, private val userDao: UserDao ) { fun find(id: Long): Single {...} } ͜ͷίϯετϥΫλΛ࢖ͬͯΠϯελϯεԽ͞ΕΔ

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

@Module abstract class ActivityModule { @ActivityScope @ContributesAndroidInjector abstract fun contributeHogeActivity(): HogeActivity } HogeActivityʹ஫ೖ͢ΔͨΊͷϝιου

Slide 56

Slide 56 text

Component • ModuleΛ࢖ͬͯґଘ͍ͯ͠ΔΦϒδΣΫτΛੜ੒ɾ஫ೖ͢Δ
 Ϋϥε

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

@Singleton @Component(modules = [ AppModule::class, ActivityModule::class, AndroidInjectionModule::class ]) interface AppComponent: AndroidInjector { @Component.Builder abstract class Builder: AndroidInjector.Builder() } Android support༻ͷΠϯλϑΣʔεΛܧঝͤ͞Δ

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

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

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

class HogeActivity : AppCompatActivity() { @Inject lateinit var presenter: HogePresenter override fun onCreate(savedInstanceState: Bundle?) { // লུ AndroidInjection.inject(this) } fun onButtonClick(view: View) { presenter.onButtonClick() } } ґଘΦϒδΣΫτΛ஫ೖ

Slide 66

Slide 66 text

ͳΔ΄ͲɺΘ͔ΒΜ • େৎ෉ • ଟ෼ΈΜͳ࠷ॳ͸෼͔ͬͯͳ͍ • ࠓޙDaggerΛಋೖ͢Δͱ͖ʹɺ΋͏Ұ౓͜ͷࢿྉ΍ଞͷαϯϓ ϧΛݟ௚ͤ͹OK

Slide 67

Slide 67 text

Koin https://insert-koin.io/

Slide 68

Slide 68 text

Koin • KotlinͰॻ͔ΕͨDIίϯςφ • ҠৡϓϩύςΟΛ࢖༻ͯ͠ґଘੑΛղܾ • Annotation processorෆ࢖༻ • Ϗϧυ଎౓ʹѱӨڹ͸ͳ͍" • ࣮ߦ͢Δ·ͰґଘπϦʔʹ໰୊͕ͳ͍͔෼͔Βͳ͍# • constructor injectionͰ͖ΔΫϥεͰ΋ɺϞδϡʔϧʹॻ͘ඞཁ͋Γ# • υΩϡϝϯτ͕Θ͔Γ΍͍͢" • AAC ViewModelʹରԠ"

Slide 69

Slide 69 text

࢖͍ํ 1. moduleͷఆٛ • ͜͜͸Dagger2ͱಉ͡ 2. startKoinͰίϯςφ࡞੒ 3. ஫ೖઌͷΫϥεͰҠৡϓϩύςΟΛ࢖ͬͯ஫ೖ͢Δ

Slide 70

Slide 70 text

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

Slide 71

Slide 71 text

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

Slide 72

Slide 72 text

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

Slide 73

Slide 73 text

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

Slide 74

Slide 74 text

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

Slide 75

Slide 75 text

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

Slide 76

Slide 76 text

class HogeActivity : AppCompatActivity() { val logger: Logger by inject() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // ... logger.v(“Hello world!”) } } ҕৡϓϩύςΟΛ࢖ͬͯ஫ೖ

Slide 77

Slide 77 text

AAC ViewModelʹରԠ • ViewModel͸Factoryܦ༝ͰΠϯελϯεΛੜ੒͢Δ • FactoryΛܧঝ͢Δ࢓૊Έ͕͋ΓɺDIͱ૊Έ߹ΘͤΔͷ͸
 τϦοΫ͕ඞཁ • Koin͸ରԠ͍ͯ͠Δʂ

Slide 78

Slide 78 text

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

Slide 79

Slide 79 text

class App : Application() { override fun onCreate() { super.onCreate() // KoinͷϞδϡʔϧͷઃఆ val appModule = module { // ... viewModel { HogeViewModel(get()) } } startKoin(this, listOf(appModule)) } } ViewModelͷΠϯελϯεԽ

Slide 80

Slide 80 text

class HogeActivity : AppCompatActivity() { val hogeViewModel: HogeViewModel by viewModel() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // ... } fun onButtonClick(view: View) { hogeViewModel.onButtonClick() } } ViewModel༻ͷ ҕৡϓϩύςΟΛ࢖͏

Slide 81

Slide 81 text

͜͜·Ͱͷ·ͱΊ • DIίϯςφ = DIΛ؆୯ʹ࣮૷͢ΔͨΊͷϥΠϒϥϦ • Dagger2…ଟػೳ͕ͩ೉͍͠ɻconstructor injectionͷ৔߹͸ ϞδϡʔϧলུՄೳɻ • Koin…Θ͔Γ΍͍͢ɻAAC ViewModelʹରԠ͍ͯ͠Δɻ

Slide 82

Slide 82 text

DIΛಋೖͯ͠Կ͕มΘͬͨʁ

Slide 83

Slide 83 text

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

Slide 84

Slide 84 text

୯ମςετ

Slide 85

Slide 85 text

// ςετର৅ͷΫϥεʢDIಋೖલʣ class UserRepository { private val apiClient: ApiClient = ApiClientImpl() fun find(id: Long): Single = apiClient.getUser(id) } ApiClientʹີ݁߹͍ͯ͠Δ

Slide 86

Slide 86 text

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

Slide 87

Slide 87 text

// UserRepositoryͷςετίʔυ class UserRepositoryTest { @Test fun find_isSuccess() { val repo = UserRepository() repo.find(1) .test() .awaitCount(1) .assertValue(expected) } } ApiClientImplͷ
 ίʔυ΋࣮ߦ͍ͯ͠Δ

Slide 88

Slide 88 text

HogeActivity HogeViewModel UserRepository ApiClient ͕͜͜ςετର৅ʹ ͳͬͯ͠·͍ͬͯΔ

Slide 89

Slide 89 text

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

Slide 90

Slide 90 text

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

Slide 91

Slide 91 text

// ςετର৅ͷΫϥεʢDIಋೖޙʣ class UserRepository( private val apiClient: ApiClient ) { fun find(id: Long): Single = apiClient.getUser(id) } ApiClientΛ ֎෦͔Β஫ೖՄೳ

Slide 92

Slide 92 text

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

Slide 93

Slide 93 text

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

Slide 94

Slide 94 text

// UserRepositoryͷςετίʔυ class UserRepositoryTest { @Test fun find_isSuccess() { val apiClient = mockk { every { getUser(any()) } returns Single.just(user) } val repo = UserRepository(apiClient) repo.find(1) .test() .awaitCount(1) .assertValue(user) } } ϞοΫΛ஫ೖͯ͠ ςετ࣮ߦ

Slide 95

Slide 95 text

݁߹ςετ

Slide 96

Slide 96 text

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

Slide 97

Slide 97 text

ྫ: EspressoͰUIςετΛॻ͘ • APIΫϥΠΞϯτͷԠ౴͕ຖճҧ͏ͷͰਏ͍ • Ϩεϙϯε͕࣌ؒʹΑͬͯҧ͏ • Ԡ౴࣌ؒ • Α͘API͕յΕΔʢ։ൃ؀ڥͳͲʣ • DIΛಋೖ͍ͯ͠Δͱɺ؆୯ʹAPIΫϥΠΞϯτΛςετ༻ͷϞοΫʹࠩ͠ସ͑ Δ͜ͱ͕Ͱ͖Δ

Slide 98

Slide 98 text

DIίϯςφͷϞδϡʔϧΛςετ༻ʹ
 ࠩ͠ସ͑Δ 1. ςετ༻ͷApplicationΛ࡞੒͠ɺ಺෦Ͱςετ༻Ϟδϡʔϧ Λ࢖ͬͯґଘπϦʔΛ࡞Δ 2. Instrumented test࣮ߦ࣌ʹىಈ͢ΔApplicationΫϥεΛɺ
 1Ͱ࡞ͬͨΫϥεʹࠩ͠ସ͑Δ

Slide 99

Slide 99 text

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

Slide 100

Slide 100 text

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

Slide 101

Slide 101 text

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

Slide 102

Slide 102 text

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

Slide 103

Slide 103 text

class TestApp : App() { override val apiModule = module { factory { val user = User(123, “kobakei”) val apiClient = mockk { every { getUser(any()) } returns Single.just(user) } apiClient } } } ϞοΫͷAPIΫϥΠΞϯτΛฦ͢ ϞδϡʔϧͰΦʔόʔϥΠυ

Slide 104

Slide 104 text

// ΞϓϦέʔγϣϯΛࠩ͠ସ͑ΔͨΊͷϥϯφʔ class MyTestRunner : AndroidJUnitRunner() { override fun newApplication(cl: ClassLoader?, name: String?, c: Context?) = super.newApplication(cl, TestApp::class.java.name, c) } TestAppΫϥεΛىಈ͢Δ

Slide 105

Slide 105 text

// app/build.gradle android { defaultConfig { testInstrumentationRunner “your.app.MyTestRunner” } } Instrumented test࣌ͷϥϯφʔΛࢦఆ

Slide 106

Slide 106 text

ຊ೔ͷ·ͱΊ

Slide 107

Slide 107 text

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

Slide 108

Slide 108 text

Thanks!!!