Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
今日から始める依存性の注入 / First Time Dependency Injection
Search
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
Keisuke Kobayashi
February 08, 2019
Programming
7.7k
26
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
今日から始める依存性の注入 / First Time Dependency Injection
DroidKaigi 2019
Room1, 2019/02/08 14:50~15:20
Keisuke Kobayashi
February 08, 2019
More Decks by Keisuke Kobayashi
See All by Keisuke Kobayashi
AI 1st でエンタープライズ SaaS を立ち上げる / AI 1st Enterprise SaaS
kobakei
1
240
プロダクト開発をAI 1stに変革する〜SaaS is dead時代で生き残るために〜 / AI 1st Product Development
kobakei
0
2.7k
iOSアプリの技術的負債をどう返済したか / How to repay the technical debt of iOS app
kobakei
2
1k
iOSアプリ内で不正なSSL証明書を検知する / SSL Pinning for iOS apps
kobakei
34
12k
Kyashアプリ開発の現場
kobakei
4
3k
Review of Google I/O 2017 & Prepare for Google I/O 2018
kobakei
0
350
APIクライアントをCodableで置き換えた話
kobakei
0
1.6k
開発者が知っておきたい通知の歴史
kobakei
9
7.9k
mockito-kotlin
kobakei
1
550
Other Decks in Programming
See All in Programming
Webフレームワークの ベンチマークについて
yusukebe
0
170
A2UI という光を覗いてみる
satohjohn
1
140
セキュリティの専門家じゃなくてもできる。「セキュリティ意識」をアップデートして サプライチェーン攻撃への耐性を高めよう。
tk3fftk
5
770
Signal Forms: Beyond the Basics @ngBaguette 2026 in Paris
manfredsteyer
PRO
0
250
OSもどきOS
arkw
0
570
脅威をエンジニアリングの糧にして――現場編 / Turning Threats into Engineering Fuel — Field Edition
nrslib
0
280
Spring Security 実践 ─ GraphQL APIで実務に役立つ 認証・認可 を学ぶ
wagyu
0
240
代数的データ型って何が嬉しいの? #frontend_phpcon_do
kajitack
8
3.7k
[2026年度第1回ORセミナー] 計画最適化ベンチャーと競技プログラミング人材
terryu16
0
260
決定論的オーケストレーションの設計と実装 / Design and Implementation of Deterministic Orchestration
nrslib
4
1.4k
ふつうのFeature Flag実践入門
irof
7
4k
Strategic Design in the Frontend: Moduliths & Micro Frontends @DDDEurope
manfredsteyer
PRO
0
100
Featured
See All Featured
Pawsitive SEO: Lessons from My Dog (and Many Mistakes) on Thriving as a Consultant in the Age of AI
davidcarrasco
0
160
Balancing Empowerment & Direction
lara
6
1.2k
Leadership Guide Workshop - DevTernity 2021
reverentgeek
1
300
Building Applications with DynamoDB
mza
96
7.1k
Building the Perfect Custom Keyboard
takai
2
800
The Limits of Empathy - UXLibs8
cassininazir
1
360
Money Talks: Using Revenue to Get Sh*t Done
nikkihalliwell
0
250
The SEO Collaboration Effect
kristinabergwall1
1
490
Building a Scalable Design System with Sketch
lauravandoore
463
34k
Darren the Foodie - Storyboard
khoart
PRO
3
3.4k
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
PRO
201
75k
Chasing Engaging Ingredients in Design
codingconduct
0
220
Transcript
ࠓ͔Β࢝ΊΔґଘੑͷೖ First Time Dependency Injection Keisuke Kobayashi DroidKaigi 2019 /
Room1, 2019/02/08 14:50~15:20
ࣗݾհ • Keisuke Kobayashi • Twitter: @kobakei122 • GitHub: @kobakei
• Merpay, Inc. / Engineering Manager • Studyplus, Inc. / ٕज़ސʢ෭ۀʣ
ࠓͷςʔϚ • DIॳ৺ऀɺ·ͨʮงғؾͰDagger2Λ͍ͬͯΔਓʯʹɺDI ͱͲ͏͍͏ͷ͔ɺԿͷͨΊʹಋೖ͢Δͷ͔Λղઆ͢Δηο γϣϯͰ͢ • ݸผͷDIίϯςφͷৄ͍͍͠ํʹ࣌ؒͷ্ؔਂೖΓ͠· ͤΜ
ΞδΣϯμ • Dependency Injection(DI)ͱʁ • AndroidΞϓϦ։ൃΛྫʹհ • DIίϯςφͷجຊతͳ͍ํ • Dagger2
& Koin • DIಋೖޙͷςετͷॻ͖ํ
Dependency Injection (DI)ͱʁ • ίϯϙʔωϯτؒͷґଘؔΛιʔείʔυ͔Βഉআ͠ɺ֎෦ ͔ΒґଘίϯϙʔωϯτΛೖͤ͞ΔσβΠϯύλʔϯ • ґଘؔΛഉআ͢Δ͜ͱͰɺίϯϙʔωϯτ͕ؒૄ݁߹ʹͳΔ
DIͷϝϦοτ • ίϯϙʔωϯτ͕ؒૄ݁߹ʹͳΔ͜ͱͰɺҎԼͷϝϦοτ͕͋ Δ • ΞϓϦέʔγϣϯΛ֦ு͘͢͠ͳΔ • ςετ͕ॻ͖͘͢ͳΔ • ͜ΕޙͰৄ͘͠ղઆ
AndroidΞϓϦ։ൃͰ Α͋͘ΔέʔεΛߟ͑Δ
Α͋͘ΔMVPͷྫ HogeActivity HogePresenter UserRepository ApiClient UserDao
Α͋͘ΔMVPͷྫ HogeActivity HogePresenter UserRepository ApiClient UserDao
// 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)...
// 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)... ґଘΦϒδΣΫτͱ ີ݁߹͍ͯ͠Δ
// 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)...
// 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)... ίϯετϥΫλͰ ґଘΦϒδΣΫτΛ ड͚औΔ
// 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)... ͏ଆ͕ґଘΦϒδΣΫτ͔Β Έཱ͍ͯͯΔ
// ґଘΦϒδΣΫτΛղܾ͢ΔͨΊͷίϯςφΦϒδΣΫτ object Container { val apiClient: ApiClient = ApiClientImpl()
val userDao: UserDao = UserDaoImpl() val userRepo: UserRepository = UserRepository(apiClient, userDao) }
// 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ͷґଘؔ ͕ഉআ͞Εͨ
Α͋͘ΔMVPͷྫ HogeActivity HogePresenter UserRepository ApiClient UserDao
Activityͷ߹ • ίϯετϥΫλͰͷೖ͕Ͱ͖ͳ͍ • Fragment, Service, BroadcastReceiverͳͲಉ͡
// DIΛಋೖ͍ͯ͠ͳ͍ίʔυ class HogeActivity : AppCompatActivity() { private val presenter
= HogePresenter() override fun onCreate(savedInstanceState: Bundle?) { // লུ } fun onButtonClick(view: View) { presenter.onButtonClick() } } Activity͕ HogePresenterʹ ີ݁߹͍ͯ͠Δ
// DIΛಋೖ͍ͯ͠ͳ͍ίʔυ class HogeActivity : AppCompatActivity() { private lateinit var
presenter: HogePresenter override fun onCreate(savedInstanceState: Bundle?) { // লུ presenter = HogePresenter(applicationContext) } fun onButtonClick(view: View) { presenter.onButtonClick() } } ͜Εີ݁߹͍ͯ͠Δ
// ґଘΦϒδΣΫτΛղܾ͢ΔͨΊͷίϯςφΦϒδΣΫτ 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ΛҾʹऔΔϝιουΛՃ
// 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ΛͤΔ
͜͜ͰؾʹͳΔ͜ͱ͕
PresenterͷϥΠϑαΠΫϧ͜ΕͰେৎʁ • PresenterActivityͱಉ͡ϥΠϑαΠΫϧʹ͍ͨ͠ • Activity͕ੜ͞ΕΔͱPresenterੜ͞ΕɺActivity͕ഁغ ͞ΕΔͱಉ࣌ʹഁغ͞ΕΔ͖ • ActivityͷΠϯελϯε͕2ͭ͋Δͱ͖ɺͦΕͧΕʹ Presenter͕͍ͯ΄͍͠
είʔϓ • ґଘΦϒδΣΫτͷੜଘظؒΛද͢ • SingletonɺActivityͱಉ͡ੜଘظؒɺͳͲ • είʔϓ͝ͱʹґଘΦϒδΣΫτ͕ੜ͞Εɺ͍ճ͞ΕΔΑ ͏ͳΈ͕ඞཁ
HogeActivity HogePresenter UserRepository ApiClient UserDao FugaActivity FugaPresenter
HogeActivity HogePresenter UserRepository ApiClient UserDao FugaActivity FugaPresenter ը໘ڞ௨Ͱ͍·Θ͍ͨ͠ ֤ը໘͝ͱʹੜ͍ͨ͠
HogeActivity HogePresenter UserRepository ApiClient UserDao FugaActivity FugaPresenter Singleton Singleton Singleton
ActivityScope ActivityScope
object Container { // লུ val userRepo = UserRepository(apiClient, logger)
} class HogeActivityScopeContainer { fun resolveHogePresenter(ctx: Context): HogePresenter {…} } class FugaActivityScopeContainer { fun resolveFugaPresenter(ctx: Context): FugaPresenter {…} }
object Container { // লུ val userRepo = UserRepository(apiClient, logger)
} class HogeActivityScopeContainer { fun resolveHogePresenter(ctx: Context): HogePresenter {…} } class FugaActivityScopeContainer { fun resolveFugaPresenter(ctx: Context): FugaPresenter {…} } Singletonͳ ΫϥεͷΈ
object Container { // লུ val userRepo = UserRepository(apiClient, logger)
} class HogeActivityScopeContainer { fun resolveHogePresenter(ctx: Context): HogePresenter {…} } class FugaActivityScopeContainer { fun resolveFugaPresenter(ctx: Context): FugaPresenter {…} } HogeActivityͱಉ͡ είʔϓͷΫϥεΛฦ͢
// 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ͷ ίϯςφΛॳظԽ
// 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Λͬͯೖ
͜͜·Ͱͷ·ͱΊ • DIͱίϯϙʔωϯτؒͷґଘؔΛ֎෦ʹ͍ग़͢ύλʔϯ • ґଘίϯϙʔωϯτΛ֎෦͔Βड͚औΔΑ͏ʹ͢Δ • είʔϓΦϒδΣΫτͷੜଘظؒΛද͠ɺείʔϓ͝ͱʹίϯςφ Λ࣋ͭ • ͜͜·Ͱͷ࣮͋͘·Ͱղઆ༻ͷࡶͳͷͳͷͰҙʂ
ΑΓ࣮ફతͳ࣮ํ๏࣍Ҏ߱
DIίϯςφ
DIίϯςφ • DIΛ࣮ݱ͢ΔͨΊͷϥΠϒϥϦ • ઌఔࣗͰ࣮ͨ͠ContainerʢΑΓͬͱ͍͍ͷʣΛࣗಈ Ͱ࡞ͬͯ͘ΕΔϥΠϒϥϦ
AndroidͰ༗໊ͳDIίϯςφ • Dagger2 • Koin • Kodein • Toothpick •
Roboguice
AndroidͰ༗໊ͳDIίϯςφ • Dagger2 • Koin • Kodein • Toothpick •
Roboguice
Dagger2 https://google.github.io/dagger/
Dagger2 • ݩʑSquare͕։ൃ => ݱࡏGoogle͕fork • Annotation processorΛ༻ • ίϯύΠϧ࣌ʹґଘπϦʔʹ͕͋Δͱ͔Δ"
• ίϯετϥΫλ͕͑ΔΫϥεͷ߹ɺґଘؔͷղܾΛࣗಈੜͰ͖Δ" • Ϗϧυ͘ͳΔ# • υΩϡϝϯτ͕͍͠###
ҙʂ • Dagger2͔ͳΓଟػೳͰ͕͢ɺ࣌ؒͷ্ؔ͋ͬ͞Γ͔͠հͰ͖· ͤΜ • Dagger2ʹAndroidʹݶΒͳ͍௨ৗ൛ͱAndroidαϙʔτ൛ ʢdagger.androidʣ͕͋Δ • ࠓdagger.androidͰͷActivityͷೖΛհ •
ʮ͓·͡ͳ͍ʯ͕େྔʹग़ͯ͘ΔͷͰҙ
Dagger2ͷొਓ • Module • Component
Module • ֤ґଘΫϥεΛͲ͏ΠϯελϯεԽ͢Δ͔Λఆٛ͢ΔΫϥε • Daggerͷ߹ίϯετϥΫλΛͬͯΠϯελϯεԽ͢Δ ΫϥεࣗಈతʹੜͰ͖Δɻ ίϯετϥΫλ͕͑ͳ͍ΫϥεΛͲ͏ͬͯΠϯελϯεԽ ͢Δ͔͚ͩఆٛ͢ΕΑ͍ɻ
Α͋͘ΔMVPͷྫ HogeActivity HogePresenter UserRepository ApiClient UserDao
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> {...} }
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ʹॻ͘ඞཁ͋Γ
@Module class AppModule { @Singleton @Provides fun provideApiClient(): ApiClient {
return ApiClientImpl.Builder().build() } }
@Module class AppModule { @Singleton @Provides fun provideApiClient(): ApiClient {
return ApiClientImpl.Builder().build() } } Dagger2ͷϞδϡʔϧΛҙຯ͢Δ
@Module class AppModule { @Singleton @Provides fun provideApiClient(): ApiClient {
return ApiClientImpl.Builder().build() } }
@Module class AppModule { @Singleton @Provides fun provideApiClient(): ApiClient {
return ApiClientImpl.Builder().build() } } ͜ͷϝιουΛґଘղܾʹ͏
@Module class AppModule { @Singleton @Provides fun provideApiClient(): ApiClient {
return ApiClientImpl.Builder().build() } } ApiClientͷείʔϓ
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ʹॻ͘ඞཁ͋Γ
@Singleton class UserRepository @Inject constructor( private val apiClient: ApiClient, private
val userDao: UserDao ) { fun find(id: Long): Single<User> {...} } ͜ͷίϯετϥΫλΛͬͯΠϯελϯεԽ͞ΕΔ
@Module abstract class ActivityModule { @ActivityScope @ContributesAndroidInjector abstract fun contributeHogeActivity():
HogeActivity } dagger.androidΛ͏ͨΊͷಛघͳϞδϡʔϧ @ProvidesϝιουΛ࣋ͨͳ͍
@Module abstract class ActivityModule { @ActivityScope @ContributesAndroidInjector abstract fun contributeHogeActivity():
HogeActivity } HogeActivityʹೖ͢ΔͨΊͷϝιου
Component • ModuleΛͬͯґଘ͍ͯ͠ΔΦϒδΣΫτΛੜɾೖ͢Δ Ϋϥε
@Singleton @Component(modules = [ AppModule::class, ActivityModule::class, AndroidInjectionModule::class ]) interface AppComponent:
AndroidInjector<App> { @Component.Builder abstract class Builder: AndroidInjector.Builder<App>() }
@Singleton @Component(modules = [ AppModule::class, ActivityModule::class, AndroidInjectionModule::class ]) interface AppComponent:
AndroidInjector<App> { @Component.Builder abstract class Builder: AndroidInjector.Builder<App>() } ͜ͷίϯϙʔωϯτ͕͏ Ϟδϡʔϧͷྻ
@Singleton @Component(modules = [ AppModule::class, ActivityModule::class, AndroidInjectionModule::class ]) interface AppComponent:
AndroidInjector<App> { @Component.Builder abstract class Builder: AndroidInjector.Builder<App>() } Android support༻ͷΠϯλϑΣʔεΛܧঝͤ͞Δ
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 }
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༻ͷΠϯλϑΣʔεΛܧঝͤ͞Δ
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Λೖ
class HogeActivity : AppCompatActivity() { @Inject lateinit var presenter: HogePresenter
override fun onCreate(savedInstanceState: Bundle?) { // লུ AndroidInjection.inject(this) } fun onButtonClick(view: View) { presenter.onButtonClick() } }
class HogeActivity : AppCompatActivity() { @Inject lateinit var presenter: HogePresenter
override fun onCreate(savedInstanceState: Bundle?) { // লུ AndroidInjection.inject(this) } fun onButtonClick(view: View) { presenter.onButtonClick() } } ґଘΦϒδΣΫτʹ @InjectΛ͚͓ͯ͘
class HogeActivity : AppCompatActivity() { @Inject lateinit var presenter: HogePresenter
override fun onCreate(savedInstanceState: Bundle?) { // লུ AndroidInjection.inject(this) } fun onButtonClick(view: View) { presenter.onButtonClick() } } ґଘΦϒδΣΫτΛೖ
ͳΔ΄ͲɺΘ͔ΒΜ • େৎ • ଟΈΜͳ࠷ॳ͔ͬͯͳ͍ • ࠓޙDaggerΛಋೖ͢Δͱ͖ʹɺ͏Ұ͜ͷࢿྉଞͷαϯϓ ϧΛݟͤOK
Koin https://insert-koin.io/
Koin • KotlinͰॻ͔ΕͨDIίϯςφ • ҠৡϓϩύςΟΛ༻ͯ͠ґଘੑΛղܾ • Annotation processorෆ༻ • ϏϧυʹѱӨڹͳ͍"
• ࣮ߦ͢Δ·ͰґଘπϦʔʹ͕ͳ͍͔͔Βͳ͍# • constructor injectionͰ͖ΔΫϥεͰɺϞδϡʔϧʹॻ͘ඞཁ͋Γ# • υΩϡϝϯτ͕Θ͔Γ͍͢" • AAC ViewModelʹରԠ"
͍ํ 1. moduleͷఆٛ • ͜͜Dagger2ͱಉ͡ 2. startKoinͰίϯςφ࡞ 3. ೖઌͷΫϥεͰҠৡϓϩύςΟΛͬͯೖ͢Δ
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)) } }
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)) } } Ϟδϡʔϧͷ࡞
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
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)) } } ΦϒδΣΫτΛ ຖճ࡞͢ΔΫϥε
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Ͱґଘղܾ
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)) } } ίϯςφͷ࡞
class HogeActivity : AppCompatActivity() { val logger: Logger by inject()
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // ... logger.v(“Hello world!”) } } ҕৡϓϩύςΟΛͬͯೖ
AAC ViewModelʹରԠ • ViewModelFactoryܦ༝ͰΠϯελϯεΛੜ͢Δ • FactoryΛܧঝ͢ΔΈ͕͋ΓɺDIͱΈ߹ΘͤΔͷ τϦοΫ͕ඞཁ • KoinରԠ͍ͯ͠Δʂ
class HogeViewModel( private val userRepository: UserRepository ) : ViewModel() {…}
class App : Application() { override fun onCreate() { super.onCreate()
// KoinͷϞδϡʔϧͷઃఆ val appModule = module { // ... viewModel { HogeViewModel(get()) } } startKoin(this, listOf(appModule)) } } ViewModelͷΠϯελϯεԽ
class HogeActivity : AppCompatActivity() { val hogeViewModel: HogeViewModel by viewModel()
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // ... } fun onButtonClick(view: View) { hogeViewModel.onButtonClick() } } ViewModel༻ͷ ҕৡϓϩύςΟΛ͏
͜͜·Ͱͷ·ͱΊ • DIίϯςφ = DIΛ؆୯ʹ࣮͢ΔͨΊͷϥΠϒϥϦ • Dagger2…ଟػೳ͕͍ͩ͠ɻconstructor injectionͷ߹ ϞδϡʔϧলུՄೳɻ •
Koin…Θ͔Γ͍͢ɻAAC ViewModelʹରԠ͍ͯ͠Δɻ
DIΛಋೖͯ͠Կ͕มΘͬͨʁ
DIͷϝϦοτʢ࠶ܝʣ • ίϯϙʔωϯτ͕ؒૄ݁߹ʹͳΔ͜ͱͰɺҎԼͷϝϦοτ͕͋ Δ • ΞϓϦέʔγϣϯΛ֦ு͘͢͠ͳΔ • ςετ͕ॻ͖͘͢ͳΔ • ͜ΕޙͰৄ͘͠ղઆ
୯ମςετ
// ςετରͷΫϥεʢDIಋೖલʣ class UserRepository { private val apiClient: ApiClient =
ApiClientImpl() fun find(id: Long): Single<User> = apiClient.getUser(id) } ApiClientʹີ݁߹͍ͯ͠Δ
// UserRepositoryͷςετίʔυ class UserRepositoryTest { @Test fun find_isSuccess() { val
repo = UserRepository() repo.find(1) .test() .awaitCount(1) .assertValue(expected) } }
// UserRepositoryͷςετίʔυ class UserRepositoryTest { @Test fun find_isSuccess() { val
repo = UserRepository() repo.find(1) .test() .awaitCount(1) .assertValue(expected) } } ApiClientImplͷ ίʔυ࣮ߦ͍ͯ͠Δ
HogeActivity HogeViewModel UserRepository ApiClient ͕͜͜ςετରʹ ͳͬͯ͠·͍ͬͯΔ
HogeActivity HogeViewModel UserRepository ApiClient ຊʹςετ͍ͨ͠ͷ ͚ͩ͜͜
DIΛಋೖ͍ͯ͠Δ߹ • ґଘίϯϙʔωϯτΛೖ͢Δͱ͖ʹɺςετʹ߹ͷ͍͍ ΦϒδΣΫτΛ͏͜ͱͰɺରͷΫϥεͷΈݕূՄೳ • Mockk, MockitoͳͲͰϞοΫʹ͢Δ͜ͱ͕ଟ͍
// ςετରͷΫϥεʢDIಋೖޙʣ class UserRepository( private val apiClient: ApiClient ) {
fun find(id: Long): Single<User> = apiClient.getUser(id) } ApiClientΛ ֎෦͔ΒೖՄೳ
// 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) } }
// 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ͷϞοΫΛ࡞
// 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) } } ϞοΫΛೖͯ͠ ςετ࣮ߦ
݁߹ςετ
݁߹ςετ • ???ʮͲ͏ͤ݁߹ͨ͠ঢ়ଶͰςετ͢Δ͔ΒDIͰίϯϙʔωϯ τؒΛૄ݁߹ʹͯ͠ҙຯͳ͍Μ͡Όͳ͍Μͷʁʯ • ͦΜͳ͜ͱͳ͍
ྫ: EspressoͰUIςετΛॻ͘ • APIΫϥΠΞϯτͷԠ͕ຖճҧ͏ͷͰਏ͍ • Ϩεϙϯε͕࣌ؒʹΑͬͯҧ͏ • Ԡ࣌ؒ • Α͘API͕յΕΔʢ։ൃڥͳͲʣ
• DIΛಋೖ͍ͯ͠Δͱɺ؆୯ʹAPIΫϥΠΞϯτΛςετ༻ͷϞοΫʹࠩ͠ସ͑ Δ͜ͱ͕Ͱ͖Δ
DIίϯςφͷϞδϡʔϧΛςετ༻ʹ ࠩ͠ସ͑Δ 1. ςετ༻ͷApplicationΛ࡞͠ɺ෦Ͱςετ༻Ϟδϡʔϧ ΛͬͯґଘπϦʔΛ࡞Δ 2. Instrumented test࣮ߦ࣌ʹىಈ͢ΔApplicationΫϥεΛɺ 1Ͱ࡞ͬͨΫϥεʹࠩ͠ସ͑Δ
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)) } }
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ΫϥεΛ ܧঝͯ͠࡞ΕΔΑ͏ʹ͢Δ
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)) } } ͜͜ΦʔόʔϥΠυ Ͱ͖ΔΑ͏ʹ͢Δ
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 } } }
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ΫϥΠΞϯτΛฦ͢ ϞδϡʔϧͰΦʔόʔϥΠυ
// ΞϓϦέʔγϣϯΛࠩ͠ସ͑ΔͨΊͷϥϯφʔ class MyTestRunner : AndroidJUnitRunner() { override fun newApplication(cl:
ClassLoader?, name: String?, c: Context?) = super.newApplication(cl, TestApp::class.java.name, c) } TestAppΫϥεΛىಈ͢Δ
// app/build.gradle android { defaultConfig { testInstrumentationRunner “your.app.MyTestRunner” } }
Instrumented test࣌ͷϥϯφʔΛࢦఆ
ຊͷ·ͱΊ
·ͱΊ • DI = ίϯϙʔωϯτؒͷґଘؔΛ֎෦ʹ͍ग़͢σβΠϯύ λʔϯ • DIΛಋೖ͢Δ͜ͱͰɺίϯϙʔωϯτ͕ૄ݁߹ʹͳΓɺίϯ ϙʔωϯτΛࠩ͠ସ͑ͨΓɺςετ͕ॻ͖͘͢ͳΔ •
Dagger2KoinͳͲDIίϯςφΛར༻͠Α͏
Thanks!!!