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
💪🏻Activityを改善した話 at DroidKaigi 2017
Search
Moyuru Aizawa
March 10, 2017
Programming
6
2.4k
💪🏻Activityを改善した話 at DroidKaigi 2017
DroidKaigi2017
TwitterID変えました。@lvla0805 -> @MoyuruAizawa
Moyuru Aizawa
March 10, 2017
Tweet
Share
More Decks by Moyuru Aizawa
See All by Moyuru Aizawa
BLUETOOTH_SCAN and iBeacon
lvla
1
88
graphicsLayer
lvla
0
200
BluetoothDevice.getName()に裏切られた話
lvla
0
340
Jetpack Composeで画像クロップ機能を実装する
lvla
0
1.1k
Jetpack Compose drag gesture and pinch gesture
lvla
1
3.8k
Jetpack Compose Layout API
lvla
1
650
BLEを使ったアプリを継続的に開発するために
lvla
0
1k
RecyclerView.ItemAnimator
lvla
1
310
RecycledViewPool
lvla
1
210
Other Decks in Programming
See All in Programming
Passkeys for Java Developers
ynojima
3
880
Cursor AI Agentと伴走する アプリケーションの高速リプレイス
daisuketakeda
1
120
Spring gRPC で始める gRPC 入門 / Introduction to gRPC with Spring gRPC
mackey0225
2
520
iOSアプリ開発で 関数型プログラミングを実現する The Composable Architectureの紹介
yimajo
2
210
DroidKnights 2025 - 다양한 스크롤 뷰에서의 영상 재생
gaeun5744
3
300
Java on Azure で LangGraph!
kohei3110
0
170
C++20 射影変換
faithandbrave
0
500
関数型まつりレポート for JuliaTokai #22
antimon2
0
130
なんとなくわかった気になるブロックテーマ入門/contents.nagoya 2025 6.28
chiilog
1
150
ドメインモデリングにおける抽象の役割、tagless-finalによるDSL構築、そして型安全な最適化
knih
11
2k
ReadMoreTextView
fornewid
1
450
Enterprise Web App. Development (2): Version Control Tool Training Ver. 5.1
knakagawa
1
120
Featured
See All Featured
How GitHub (no longer) Works
holman
314
140k
Scaling GitHub
holman
459
140k
Building a Modern Day E-commerce SEO Strategy
aleyda
41
7.3k
Build The Right Thing And Hit Your Dates
maggiecrowley
36
2.8k
GitHub's CSS Performance
jonrohan
1031
460k
Raft: Consensus for Rubyists
vanstee
140
7k
RailsConf 2023
tenderlove
30
1.1k
The Myth of the Modular Monolith - Day 2 Keynote - Rails World 2024
eileencodes
26
2.8k
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
657
60k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
30
2.1k
Learning to Love Humans: Emotional Interface Design
aarron
273
40k
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
31
2.4k
Transcript
!ActivityΛվળͨ͠ @lvla0805
MWMB MWMB Ѫᖒ๖ (Moyuru Aizawa) - Lead Kotlin engineer of
Pairs Div. Eureka, Inc.
- Pairs - Couples - a member of IAC/Match Group
Pairs
Pairs
·ͣཧͷ͓૬खΛ୳͠·͢
ؾʹͳΔ͓૬खʹ͍͍Ͷ!ΛૹΔ
Ϛονϯάޙɺϝοηʔδަ
αϯϓϧίʔυʹؔͯ͠
PairsJava/Kotlin͕ࠞࡏ͍ͯ͠·͢ɻ Αͬͯɺαϯϓϧίʔυʹ͓͍ͯ྆ ݴޠ͕ར༻͞Ε͍ͯ·͢ɻ
Kotlin͕ۤखͳϑϨϯζ ͷΈͳ͞Μ
ͱ͠ΐ͔ΜͰKotlinʹ͍ͭ ͯ͠Β·͠ΐʔ!
Instanceੜ final Serval serval = new Serval(); Kaban kaban =
new Kaban(); val serval = Serval() var kaban = Kaban()
Instanceੜ final Serval serval = new Serval(); Kaban kaban =
new Kaban(); val serval = Serval() var kaban = Kaban()
Instanceੜ final Serval serval = new Serval(); Kaban kaban =
new Kaban(); val serval = Serval() var kaban = Kaban()
Instanceੜ final Serval serval = new Serval(); Kaban kaban =
new Kaban(); val serval = Serval() var kaban = Kaban()
Class public class JapariBus { private final Trailer trailer; private
final Container container; public JapariBus(Trailer trailer, Container container) { this.trailer = trailer; this.container = container; } } class JapariBus(private val trailer: Trailer, private val container: Container) Έ͔͡ʔ͍
Class public class JapariBus { private final Trailer trailer; private
final Container container; public JapariBus(Trailer trailer, Container container) { this.trailer = trailer; this.container = container; } } class JapariBus(private val trailer: Trailer, private val container: Container) Έ͔͡ʔ͍
Class public class JapariBus { private final Trailer trailer; private
final Container container; public JapariBus(Trailer trailer, Container container) { this.trailer = trailer; this.container = container; } } class JapariBus(private val trailer: Trailer, private val container: Container) Έ͔͡ʔ͍
Lambda observable.map(new Func1<Foo, Bar>() { @Override public Bar call(Foo foo)
{ return foo.toBar(); } }); observable.map(foo -> foo.toBar()); observable.map { foo -> foo.toBar() }
Lambda observable.map(new Func1<Foo, Bar>() { @Override public Bar call(Foo foo)
{ return foo.toBar(); } }); observable.map(foo -> foo.toBar()); observable.map { foo -> foo.toBar() }
Lambda observable.map(new Func1<Foo, Bar>() { @Override public Bar call(Foo foo)
{ return foo.toBar(); } }); observable.map(foo -> foo.toBar()); observable.map { foo -> foo.toBar() }
Lambda observable.map(new Func1<Foo, Bar>() { @Override public Bar call(Foo foo)
{ return foo.toBar(); } }); observable.map(foo -> foo.toBar()); observable.map { foo -> foo.toBar() }
ຊ
Pairsʹ !Activity͕͍ͬͺ͍ ͩͬͨ ͢͝ʔ͍
ར༻ٕज़ʹ͍ͭͯ
ٕज़֓ཁ Languages Java/Kotlin Network Retrofit2, OkHttp3 O/R Mapper Orma Reactive
Programming RxJava1 Dependency Injection Dagger2
!Activity?
‣ ϏδωεϩδοΫΛ͍࣋ͬͯΔ ‣ IOॲཧ͕͕ͬͭΓॻ͔Ε͍ͯΔ ‣ ͱʹ͔͘ߦ͕ଟ͍ !
ϏδωεϩδοΫ
ϏδωεϩδοΫ if (isPaidUser) { // ༗ྉձһͷΈѻ͑ΔػೳΛ༗ޮԽ͢Δ } else { //
༗ྉձһͷΈѻ͑ΔػೳΛແޮԽ͢Δ }
ϏδωεϩδοΫ if (isLikedByPartner) { // ͓૬ख͔Β͍͍Ͷ!ΛΒ͍ͬͯΔঢ়ଶ } else if (isLikedByMe)
{ // ͍͍Ͷ!Λ͍ͯ͠Δঢ়ଶ } else if (isLikedWithMessage) { // ϝοηʔδ͖͍͍Ͷ!Λ͍ͯ͠Δঢ়ଶ } else if (isLooked) { // ΈͯͶ!Λ͍ͯ͠Δঢ়ଶ } else if (isLookedWithMessage) { // ϝοηʔδ͖ΈͯͶ!Λ͍ͯ͠Δঢ়ଶ } else if (isHidden) { // ඇදࣔઃఆΛͨ͠ޙ } else if (isBlocked) { // ϒϩοΫઃఆΛͨ͠ޙ } else if (...) { ... }
‣ ͜Ε͚ͩͳΒখͦ͞͏ʹΈ͑Δ͕… ‣ ϏδωεϩδοΫ͕ॏͳΓ߹ͬͯɺΞϓϦέʔγϣ ϯ͕࡞ΒΕΔ ‣ ෳͷϏδωεϩδοΫ͕Activity/Fragment/Adapter ʹ͋;Εͯ͘ΔͱɺίʔυΛ͏ͷ͕ࠔʹ… ϏδωεϩδοΫ
IO
IO userClient.fetchMathingUsers() .subscribeOnIo() .doOnNex { users -> userDao.insertAll(users) } .onError
{ userDao.findAll() } .observeOnMain() .subscribe( { users -> showUsers(users) }, { ... })
ͱʹ͔͘ߦ͕ଟ͍ɺͭΒ͍
ఢ!ActivityͷΈͳΒͣ
‣ ΊͪΌͪ͘Όͳ ‣ ਆΫϥε ‣ static͓͡͞Μ ͭΒΈ
ΊͪΌͪ͘Όͳ
‣ Converter ‣ JsonΛϚοϐϯάͨ͠Ϋϥε͔ΒɺΞϓϦͰ ͍͍͢σʔλΫϥεʹมΛߦ͏Ϋϥε ‣ มΛߦ͏͜ͱͷΈ͕ͷͣ ΊͪΌͪ͘Όͳ class UserConverter
{ fun convert(res: MatchingUsersResponse): List<User> { return res.users.map { resUser -> User(resUser.name, …) } } }
ͳͷ͕ͩ….
‣ Convert͢ΔաఔͰϢʔβʔͷϑΟϧλϦϯάΛߦ͏ Converter ΊͪΌͪ͘Όͳ class UserConverter { fun convert(res: MatchingUsersResponse):
List<User> { return res.users.map { user -> User(user.name, user.age, ...) } .filter { … } } }
None
ਆΫϥε
XxxManager ਆΫϥε
‣ SearchManager ‣ ݕࡧ݅ͷอ࣋ ‣ ݕࡧ݁Ռͷอ࣋ ‣ ݕࡧ݁ՌΛऔಘ͢Δॲཧͷཧ ‣ …
ਆΫϥε
‣ ਆʹک͑ ‣ ਆʹفΓ ‣ ਆʹ໋Λ๋͛Δʑ ਆΫϥε
Static͓͡͞Μ
‣ APIDatabaseΛૢ࡞͢ΔϝιουͳͲɺ͍ͨΔͱ͜ ΖͰstaticϝιουΛར༻࣮ͨ͠ʹͳ͍ͬͯͨɻ static͓͡͞Μ
static͓͡͞Μ public static Observable<List<User>> fetchMatchingUsers() { return PairsClient.getRetrofit() .create(MatchingUsersService.class) .fetch()
... }
static͓͡͞Μ public static Observable<List<User>> fetchMatchingUsers() { return PairsClient.getRetrofit() .create(MatchingUsersService.class) .fetch()
... }
static͓͡͞Μ public static Observable<List<User>> fetchMatchingUsers() { return PairsClient.getRetrofit() .create(MatchingUsersService.class) .fetch()
... }
static͓͡͞Μ public static Observable<List<User>> fetchMatchingUsers() { return PairsClient.getRetrofit() .create(MatchingUsersService.class) .fetch()
... }
‣ ύϑΥʔϚϯε͕ΑΖ͘͠ͳ͍ ‣ “staticϝιουͰstaticϝιουΛͬͯRetrofitΛ औಘ͠ɺInterfaceͷ࣮ͷੜΛߦ͏”ͱ͍͏ӅΕͨ ೖྗ͕͋Δɻ ‣ ӅΕ͍ͯͯϞοΫ͕Ͱ͖ͳ͍ ‣ UnitςετΉ͔͍ͣ͠…
static͓͡͞Μ
static͓͡͞Μ class MatchingUsersClient( private val service: MatchingUsersService) { fun fetch():
Observable<List<User>> { return service.fetch()..... } }
͍!Ͳ͏ʹ͔͠ͳ͍ͱ!
EMERGENCY!!
‣ Retrofit1->2ͷҠߦ࣌ ‣ ຊདྷPOST͖͢ͱ͜ΖΛGETʹ͍ͯͨ͠ ‣ ॏཁͳػೳ͕͑ͳ͍ঢ়ଶʹ Emergency
‣ ࠓޙಉࣦ͡ഊΛ܁Γฦ͞ͳ͍ͨΊʹ? ‣ खಈͷςετ૿͢ͷҙຯ͕ແ͍ΑͶ ‣ ςετίʔυॻ͍ͯࣗಈԽ͍ͨ͠ΑͶɻ Emergency
ςετίʔυ ॻ͖·͠ΐ͏!
…
ॻ͚ͳ͘ͳ͍?
‣ ӅΕͨೖྗग़ྗ ‣ ؔΘΓ߹͏staticϝιου ‣ DBʹґଘ͍ͯ͠ΔClient ‣ ෳͷׂΛ࣋ͪɺ༷ʑͳঢ়ଶΛ࣋ͭਆʑ ‣ Activityʹॻ͔ΕͨϏδωεϩδοΫୡ
ςετͰ͖ͳ͍ΑͶ
‣ ͷׂ ‣ !Activityͷվળ ‣ ϏδωεϩδοΫͷഉআ ‣ IOͷഉআ ‣ Staticϝιου܈ͱਆʑʹҾୀ͍ͯͨͩ͘͠
՝
େʑతϦϑΝΫλϦϯά
Before Entity/Dao Database API Client Activity / Fragment
‣ ͝ͱʹϨΠϠʔΛ͚Δ ‣ Retrofit͕ੜ͢ΔInterfaceͷ࣮Λհͯ͠ɺServerͱΓऔΓ Λߦ͏Client ‣ DatabaseͱΓऔΓΛߦ͏Dao ‣ σʔλιʔεΛӅṭ͠ɺσʔλͷอଘ/औಘΛߦ͏Repository ‣
ϏδωεϩδοΫΛ࣮ݱ͢ΔUseCase ‣ UseCaseͱViewΛͭͳ͙Presenter ‣ ֤ϨΠϠʔObservableΛհͯ͠ΓऔΓΛߦ͏ ͷׂ
After Dao Database API Client Repository UseCase Presenter Activity /
Fragment Service
After Dao Client Repository UseCase Presenter Activity / Fragment Retrofit͕࣮͢ΔInterface
Database API Service
After Dao Client Repository UseCase Presenter Activity / Fragment Serverͱͷσʔλૹड৴
Database API Service
After Dao Client Repository UseCase Presenter Activity / Fragment DBͱͷσʔλૹड৴
Database API Service
After Dao Repository UseCase Presenter Activity / Fragment Client σʔλऔಘ/อଘ
Database API Service
After Dao Repository UseCase Presenter Activity / Fragment Client ϏδωεϩδοΫ
Database API Service
After Dao Repository UseCase Presenter Activity / Fragment Client ϏδωεϩδοΫͷར༻
Activity/Fragmentͷૢ࡞ Database API Service
After Dao Repository UseCase Presenter Activity / Fragment Client Viewͷૢ࡞
Activityґଘͷػೳͷૢ࡞ Database API Service
After Dao Client Repository UseCase Presenter Activity / Fragment Observable
Observable Observable Observable Observable Observable Database API Service
Matching
Client Dao Client Repository UseCase Presenter Activity / Fragment Database
API Service
‣ Retrofitʹੜͤͨ͞Interfaceͷ࣮Λར༻ͯ͠௨৴Λ ߦ͏ ‣ JsonͷϚοϐϯάΫϥεΛΞϓϦͰར༻͍͢͠ σʔλΫϥεʹม͢Δ Client
Client class UserClient(private val service: UserService) { fun fetchMatchingUsers(offset: Int):Observable<List<User>>
{ return service.fetchMatchingUsers(offset) .map { response -> UserConverter.convert(response) } } }
Client class UserClient(private val service: UserService) { fun fetchMatchingUsers(offset: Int):Observable<List<User>>
{ return service.fetchMatchingUsers(offset) .map { response -> UserConverter.convert(response) } } }
Client class UserClient(private val service: UserService) { fun fetchMatchingUsers(offset: Int):Observable<List<User>>
{ return service.fetchMatchingUsers(offset) .map { response -> UserConverter.convert(response) } } }
Client Test val userService = mock<UserService>() val client = UserClient(userService)
userService.invoked.thenReturn(...) client.fetchMathingUsers().test().run { ... }
Dao Dao Client Repository UseCase Presenter Activity / Fragment Database
API Service
‣ OrmaDatabaseΛར༻ͯ͠Database͔Βσʔλͷऔಘ Λߦ͏ ‣ OrmaDatabaseΛར༻ͯ͠DatabaseσʔλΛอଘ͢ Δ Dao
Dao class UserDao(private val orma: OrmaDatabase) { fun insert(users: List<User>)
{ orma.transactionSync { orma.prepareInsertIntoUser(OnConflict.REPLACE) .executeAll(contributors) } } fun findAll(): Observable<List<User>> { return orma.selectFromUser() .executeAsObservable() .toList() } }
Dao class UserDao(private val orma: OrmaDatabase) { fun insert(users: List<User>)
{ orma.transactionSync { orma.prepareInsertIntoUser(OnConflict.REPLACE) .executeAll(contributors) } } fun findAll(): Observable<List<User>> { return orma.selectFromUser() .executeAsObservable() .toList() } }
Dao class UserDao(private val orma: OrmaDatabase) { fun insert(users: List<User>)
{ orma.transactionSync { orma.prepareInsertIntoUser(OnConflict.REPLACE) .executeAll(contributors) } } fun findAll(): Observable<List<User>> { return orma.selectFromUser() .executeAsObservable() .toList() } }
Dao class UserDao(private val orma: OrmaDatabase) { fun insert(users: List<User>)
{ orma.transactionSync { orma.prepareInsertIntoUser(OnConflict.REPLACE) .executeAll(contributors) } } fun findAll(): Observable<List<User>> { return orma.selectFromUser() .executeAsObservable() .toList() } }
Dao Test val dao = UserDao(orma) dao.insert(...) orma.selectFromUser() .executeAsObservable() .toList()
.test() .run { ... } orma.insertIntoUser()… dao.findAll().test().run { ... }
Repository Dao Repository UseCase Presenter Activity / Fragment Client Database
API Service
‣ ClientͱDaoͷͲͪΒ͔͔ΒσʔλͷऔಘΛߦ͏ ‣ Clientܦ༝ͰAPI͔ΒσʔλͷऔಘΛߦͬͨ߹ʹ DatabaseʹσʔλΛอଘ͢Δ Repository
UserRepository class UserRepository( private val dao: UserDao, private val userClient:
UserClient) { fun getMatchingUsers(offset: Int):Observable<List<User>> { return userClient.fetchMatchingUsers(offset) .doOnNext { users -> dao.insert(users) } .onErrorResumeNext { dao.findAll() } } }
UserRepository class UserRepository( private val dao: UserDao, private val userClient:
UserClient) { fun getMatchingUsers(offset: Int):Observable<List<User>> { return userClient.fetchMatchingUsers(offset) .doOnNext { users -> dao.insert(users) } .onErrorResumeNext { dao.findAll() } } }
UserRepository class UserRepository( private val dao: UserDao, private val userClient:
UserClient) { fun getMatchingUsers(offset: Int):Observable<List<User>> { return userClient.fetchMatchingUsers(offset) .doOnNext { users -> dao.insert(users) } .onErrorResumeNext { dao.findAll() } } }
UserRepository class UserRepository( private val dao: UserDao, private val userClient:
UserClient) { fun getMatchingUsers(offset: Int):Observable<List<User>> { return userClient.fetchMatchingUsers(offset) .doOnNext { users -> dao.insert(users) } .onErrorResumeNext { dao.findAll() } } }
UserRepository class UserRepository( private val dao: UserDao, private val userClient:
UserClient) { fun getMatchingUsers(offset: Int):Observable<List<User>> { return userClient.fetchMatchingUsers(offset) .doOnNext { users -> dao.insert(users) } .onErrorResumeNext { dao.findAll() } } }
UserRepository class UserRepository( private val dao: UserDao, private val userClient:
UserClient) { fun getMatchingUsers(offset: Int):Observable<List<User>> { return userClient.fetchMatchingUsers(offset) .doOnNext { users -> dao.insert(users) } .onErrorResumeNext { dao.findAll() } } }
UserRepository class UserRepository( private val dao: UserDao, private val userClient:
UserClient) { fun getMatchingUsers(offset: Int):Observable<List<User>> { return userClient.fetchMatchingUsers(offset) .doOnNext { users -> dao.insert(users) } .onErrorResumeNext { dao.findAll() } } }
UseCase Dao Repository UseCase Presenter Activity / Fragment Client Database
API Service
‣ ϏδωεϩδοΫΛ࣮ݱ͢Δ UseCase
MatchingUserUseCase class MatchingUserUseCase(private val repo: UserRepository) { fun getUsers(offset: Int)
= repo.getMatchingUsers(offset) fun getFacebookPublicSetting( me: Me, user: User): FacebookPublicSetting { return if (shouldShowMeFacebook(me, user)) { if (shouldShowPartnerFacebook(user)) { FacebookPublicSetting.BOTH } else { FacebookPublicSetting.ONLY_ME } } else { if (shouldShowPartnerFacebook(user)) { FacebookPublicSetting.ONLY_USER } else { FacebookPublicSetting.NEITHER } } } }
MatchingUserUseCase class MatchingUserUseCase(private val repo: UserRepository) { fun getUsers(offset: Int)
= repo.getMatchingUsers(offset) fun getFacebookPublicSetting( me: Me, user: User): FacebookPublicSetting { return if (shouldShowMeFacebook(me, user)) { if (shouldShowPartnerFacebook(user)) { FacebookPublicSetting.BOTH } else { FacebookPublicSetting.ONLY_ME } } else { if (shouldShowPartnerFacebook(user)) { FacebookPublicSetting.ONLY_USER } else { FacebookPublicSetting.NEITHER } } } }
MatchingUserUseCase class MatchingUserUseCase(private val repo: UserRepository) { fun getUsers(offset: Int)
= repo.getMatchingUsers(offset) fun getFacebookPublicSetting( me: Me, user: User): FacebookPublicSetting { return if (shouldShowMeFacebook(me, user)) { if (shouldShowPartnerFacebook(user)) { FacebookPublicSetting.BOTH } else { FacebookPublicSetting.ONLY_ME } } else { if (shouldShowPartnerFacebook(user)) { FacebookPublicSetting.ONLY_USER } else { FacebookPublicSetting.NEITHER } } } }
MatchingUserUseCase class MatchingUserUseCase(private val repo: UserRepository) { fun getUsers(offset: Int)
= repo.getMatchingUsers(offset) fun getFacebookPublicSetting( me: Me, user: User): FacebookPublicSetting { return if (shouldShowMeFacebook(me, user)) { if (shouldShowPartnerFacebook(user)) { FacebookPublicSetting.BOTH } else { FacebookPublicSetting.ONLY_ME } } else { if (shouldShowPartnerFacebook(user)) { FacebookPublicSetting.ONLY_USER } else { FacebookPublicSetting.NEITHER } } } }
MatchingUserUseCase class MatchingUserUseCase(private val repo: UserRepository) { fun getUsers(offset: Int)
= repo.getMatchingUsers(offset) fun getFacebookPublicSetting( me: Me, user: User): FacebookPublicSetting { return if (shouldShowMeFacebook(me, user)) { if (shouldShowPartnerFacebook(user)) { FacebookPublicSetting.BOTH } else { FacebookPublicSetting.ONLY_ME } } else { if (shouldShowPartnerFacebook(user)) { FacebookPublicSetting.ONLYE_USER } else { FacebookPublicSetting.NEITHER } } } }
MatchingUserUseCase class MatchingUserUseCase(private val repo: UserRepository) { fun getUsers(offset: Int)
= repo.getMatchingUsers(offset) fun getFacebookPublicSetting( me: Me, user: User): FacebookPublicSetting { return if (shouldShowMeFacebook(me, user)) { if (shouldShowPartnerFacebook(user)) { FacebookPublicSetting.BOTH } else { FacebookPublicSetting.ONLY_ME } } else { if (shouldShowPartnerFacebook(user)) { FacebookPublicSetting.ONLY_PARTNER } else { FacebookPublicSetting.NEITHER } } } }
MatchingUserUseCase class MatchingUserUseCase(private val repo: UserRepository) { fun getUsers(offset: Int)
= repo.getMatchingUsers(offset) fun getFacebookPublicSetting( me: Me, user: User): FacebookPublicSetting { return if (shouldShowMeFacebook(me, user)) { if (shouldShowPartnerFacebook(user)) { FacebookPublicSetting.BOTH } else { FacebookPublicSetting.ONLY_ME } } else { if (shouldShowPartnerFacebook(user)) { FacebookPublicSetting.ONLY_PARTNER } else { FacebookPublicSetting.NEITHER } } } }
Presenter Dao Repository UseCase Presenter Activity / Fragment Client Database
API Service
‣ UseCaseΛར༻ͯ͠ϏδωεϩδοΫΛ࣮ݱ͢Δ ‣ ϏδωεϩδοΫͷөΛActivity/Fragmentʹରͯ͠ ߦ͏ ‣ Activity/FragmentͱPresenterͷఏڙ͢ΔContractΠ ϯλʔϑΣʔεΛհͯ͠ߦ͏ Presenter
Presenter class MatchingPresenter( private val contract: Contract, private val useCase:
MatchingUserUseCase) { fun init() { useCase.getUsers().applySchedulers() .doOnSubscribe { contract.showProgress() } .doOnUnsubscribe { contract.hideProgress() } .subscribe( { users -> contract.showMatchingUsers(users) }, { ... } ) } interface Contract { fun showMatchingUsers(users: List<User>) fun showProgress() fun hideProgress() } }
Presenter class MatchingPresenter( private val contract: Contract, private val useCase:
MatchingUserUseCase) { fun init() { useCase.getUsers().applySchedulers() .doOnSubscribe { contract.showProgress() } .doOnUnsubscribe { contract.hideProgress() } .subscribe( { users -> contract.showMatchingUsers(users) }, { ... } ) } interface Contract { fun showMatchingUsers(users: List<User>) fun showProgress() fun hideProgress() } }
Presenter class MatchingPresenter( private val contract: Contract, private val useCase:
MatchingUserUseCase) { fun init() { useCase.getUsers().applySchedulers() .doOnSubscribe { contract.showProgress() } .doOnUnsubscribe { contract.hideProgress() } .subscribe( { users -> contract.showMatchingUsers(users) }, { ... } ) } interface Contract { fun showMatchingUsers(users: List<User>) fun showProgress() fun hideProgress() } }
Presenter class MatchingPresenter( private val contract: Contract, private val useCase:
MatchingUserUseCase) { fun init() { useCase.getUsers().applySchedulers() .doOnSubscribe { contract.showProgress() } .doOnUnsubscribe { contract.hideProgress() } .subscribe( { users -> contract.showMatchingUsers(users) }, { ... } ) } interface Contract { fun showMatchingUsers(users: List<User>) fun showProgress() fun hideProgress() } }
Presenter class MatchingPresenter( private val contract: Contract, private val useCase:
MatchingUserUseCase) { fun init() { useCase.getUsers().applySchedulers() .doOnSubscribe { contract.showProgress() } .doOnUnsubscribe { contract.hideProgress() } .subscribe( { users -> contract.showMatchingUsers(users) }, { ... } ) } interface Contract { fun showMatchingUsers(users: List<User>) fun showProgress() fun hideProgress() } }
Presenter class MatchingPresenter( private val contract: Contract, private val useCase:
MatchingUserUseCase) { fun init() { useCase.getUsers().applySchedulers() .doOnSubscribe { contract.showProgress() } .doOnUnsubscribe { contract.hideProgress() } .subscribe( { users -> contract.showMatchingUsers(users) }, { ... } ) } interface Contract { fun showMatchingUsers(users: List<User>) fun showProgress() fun hideProgress() } }
Presenter class MatchingPresenter( private val contract: Contract, private val useCase:
MatchingUserUseCase) { fun init() { useCase.getUsers().applySchedulers() .doOnSubscribe { contract.showProgress() } .doOnUnsubscribe { contract.hideProgress() } .subscribe( { users -> contract.showMatchingUsers(users) }, { ... } ) } interface Contract { fun showMatchingUsers(users: List<User>) fun showProgress() fun hideProgress() } }
Presenter class MatchingPresenter( private val contract: Contract, private val useCase:
MatchingUserUseCase) { fun init() { useCase.getUsers().applySchedulers() .doOnSubscribe { contract.showProgress() } .doOnUnsubscribe { contract.hideProgress() } .subscribe( { users -> contract.showMatchingUsers(users) }, { ... } ) } interface Contract { fun showMatchingUsers(users: List<User>) fun showProgress() fun hideProgress() } }
Activity, Fragment Dao Repository UseCase Presenter Activity / Fragment Client
Database API Service
‣ Viewͷૢ࡞ ‣ Activityʹґଘͨ͠ػೳͷϋϯυϦϯά ‣ ͜ΕΒΛPresenterͷ࣋ͭContractΠϯλʔϑΣʔεΛ հͯ͠ఏڙ͢Δ Activity/Fragment
Activity/Fragment class MatchingFragment: Fragment(), MatchingPresenter.Contract { … override fun onViewCreated(...)
{ … presenter.init() } override fun showMatchingUsers(users: List<User>) { listView.adapter = MatchingUsersAdapter(activity, users) } override fun showProgress() { progress.toVisible() } override fun hideProgress() { progress.toGone() } }
Activity/Fragment class MatchingFragment: Fragment(), MatchingPresenter.Contract { … override fun onViewCreated(...)
{ … presenter.init() } override fun showMatchingUsers(users: List<User>) { listView.adapter = MatchingUsersAdapter(activity, users) } override fun showProgress() { progress.toVisible() } override fun hideProgress() { progress.toGone() } }
Activity/Fragment class MatchingFragment: Fragment(), MatchingPresenter.Contract { … override fun onViewCreated(...)
{ … presenter.init() } override fun showMatchingUsers(users: List<User>) { listView.adapter = MatchingUsersAdapter(activity, users) } override fun showProgress() { progress.toVisible() } override fun hideProgress() { progress.toGone() } }
Dependency Injection
ґଘؔGraph Dao Client Repository UseCase Presenter Activity / Fragment Orma
Context Service Retrofit OkHttp
‣ ΫϥεͰґଘ͢ΔΫϥεͷΠϯελϯεੜΛߦ͏ ͷྑ͘ͳ͍ ‣ ςετ࣌ʹΓସ͑Δͱ͍ͬͨࣄ͕Ͱ͖ͳ͍ ‣ ֎Ͱੜ͠ɺίϯετϥΫλ·ͨϝϯόʔʹରͯ͠ ґଘੑΛ͍ΕΔ ‣ ґଘੑΛཧ͢ΔΫϥεΛ࡞Δ
‣ ґଘੑΛཧ͢ΔΫϥεͷ࣮/ϝϯς͕େม ‣ DIΛ͓͏ -> Dagger2 DI
Α͠! ͜Μͳײ͡Ͱߦ͜͏!
‣ Pairs JP AndroidνʔϜ࣌3ਓ ‣ 2ਓͦͷ··ࢪࡦͷ࣮ ‣ ࢲ͕୲ ‣ Pairs
Globalͷ@yuyakaidoʹखͬͯΒ͏ ‣ ܭ 1.5ਓ͘Β͍Ͱ࣮ Ͳ͏ਐΊ͍ͯͬͨͷ͔
‣ ϝΠϯετϦʔϜͱผʹrefactorϒϥϯν ‣ ϝΠϯετϦʔϜͱͷίϯϑϦΫτΛආ͚ΔͨΊʹ Activity/Fragmentͷมߋඞཁ࠷ݶʹ͑ͨ ‣ PresenterΛ࣮͠ɺActivity/FragmentʹContractΠ ϯλʔϑΣʔεΛ࣮͠Α͏ͱࢥ͏ͱɺมߋ͕ େʹͳΔͷͰPresenterͷ࣮ޙճ͠ʹ ‣
Activity/FragmentҰ୴UseCaseΛͰѻ͏͜ͱΛ ڐ༰ Ͳ͏ਐΊ͍ͯͬͨͷ͔
Ͳ͏ਐΊ͍ͯͬͨͷ͔ ‣ ґଘؔGraphͷϧʔτ͔Βมߋ ΛՃ͍͑ͯ͘ ‣ ·ͣDagger2ͷModule/ ComponentΛ࡞ɻ ‣ OkHttp, Retrofit,
Ormaͷprovideϝ ιουΛ࣮
‣ σʔλιʔε͝ͱʹDao, Client, RepositoryΛ࣮ ‣ UserDao, UserClient, UserRepository ‣ MeDao,
MeClient, MeRepository ‣ … ‣ σʔλιʔε͝ͱʹɺRepository͕࣮ྃ࣍ୈ UseCaseΛ࣮ ‣ Activity/FragmentʹมߋΛՃ͑Δ Ͳ͏ਐΊ͍ͯͬͨͷ͔ ‘͓૬ख’ͷσʔλΫϥε ‘ࣗ’ͷσʔλΫϥε
‣ ͍͖ͳΓΰʔϧΛࢦ͞ͳ͍ ‣ ग़དྷΔݶΓਖ਼ৗಈ࡞͢Δঢ়ଶΛอͭ ‣ খ͍͞มߋΛੵΈॏͶ͍ͯ͘ ‣ ΠϯλʔϑΣʔε͕มΘΔͱݺͼग़͠ݩͷมߋඞ ཁʹͳΔͷͰɺҰ୴ճΓಓͯ͠తΛୡ͢Δ ‣
ͦͷޙʹΠϯλʔϑΣʔεΛमਖ਼ͯ͠ݺͼग़͠ݩ ͷมߋΛߦ͍ͬͯ͘ େͳ͜ͱ
‣ ৽͍͠ઃܭʹؔͯ͠νʔϜϝϯόʔ͕ཧղͰ͖Δ·Ͱ ͬ͘͡Γͱڭ͑Δ ‣ ͍ͭͰಡΈฦͤΔΑ͏ʹɺQiita:teamͳͲʹ·ͱΊ ͓ͯ͘ େͳ͜ͱ
1.5ϲ݄ޙ…
Release
վળޙ
‣ ςετίʔυ͕֨ஈʹॻ͖͘͢ͳͬͨ ‣ શମͷݟ௨͕͠ྑ͘ͳͬͨ ‣ ԿॲʹɺԿΛॻ͚Α͍ͷ͔͕͔Γ͍͢ ‣ ԿॲʹɺԿ͕͋Δͷ͔͕͔Γ͍͢ ‣ ϥΠϒϥϦͷࡌͤସ͕͑༰қʹͳΓͦ͏
‣ e.g. ORMΛࡌͤସ͍͑ͨͱͳͬͨΒDaoͷมߋ ͚ͩͰ݁͢Δ ͜ͷ࣮ͷϝϦοτ
‣ Ϋϥεɺϝιου͕ଟ͍ ‣ Dex Countࢯʙ ‣ ࣮ίετ ‣ ֶशίετ ͜ͷ࣮ͷσϝϦοτ
Conclusion
‣ Dao, Client, Repository, UseCaseͳͲ͝ͱʹϨΠ ϠʔΛ͚ͨ ‣ ModelͱViewͷؒʹPresenterΛઃ͚ͯViewͱModelΛ ໌֬ʹ͚Δ ‣
Dagger2ʹґଘੑͷղܾΛҕৡ͢Δ ‣ σʔλιʔε͝ͱʹԼͷϨΠϠʔ͔Βॱʹ࣮͍ͯ͠ ͘ ‣ ϝϯόʔͷϨΫνϟʔ͔ܽ͞ͳ͍ ‣ จͱͯ͠͠ɺ͍ͭͰݟฦ͢Α͏ʹͨ͠ ·ͱΊ
Thank you