$30 off During Our Annual Pro Sale. View Details »
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
120
graphicsLayer
lvla
0
240
BluetoothDevice.getName()に裏切られた話
lvla
0
380
Jetpack Composeで画像クロップ機能を実装する
lvla
0
1.2k
Jetpack Compose drag gesture and pinch gesture
lvla
1
4k
Jetpack Compose Layout API
lvla
1
680
BLEを使ったアプリを継続的に開発するために
lvla
0
1.1k
RecyclerView.ItemAnimator
lvla
1
330
RecycledViewPool
lvla
1
240
Other Decks in Programming
See All in Programming
リリース時」テストから「デイリー実行」へ!開発マネージャが取り組んだ、レガシー自動テストのモダン化戦略
goataka
0
130
宅宅自以為的浪漫:跟 AI 一起為自己辦的研討會寫一個售票系統
eddie
0
510
20 years of Symfony, what's next?
fabpot
2
360
WebRTC と Rust と8K 60fps
tnoho
2
2k
テストやOSS開発に役立つSetup PHP Action
matsuo_atsushi
0
160
tsgolintはいかにしてtypescript-goの非公開APIを呼び出しているのか
syumai
7
2.2k
S3 VectorsとStrands Agentsを利用したAgentic RAGシステムの構築
tosuri13
6
310
20251212 AI 時代的 Legacy Code 營救術 2025 WebConf
mouson
0
170
20251127_ぼっちのための懇親会対策会議
kokamoto01_metaps
2
440
エディターってAIで操作できるんだぜ
kis9a
0
730
非同期処理の迷宮を抜ける: 初学者がつまづく構造的な原因
pd1xx
1
720
実は歴史的なアップデートだと思う AWS Interconnect - multicloud
maroon1st
0
170
Featured
See All Featured
The Art of Programming - Codeland 2020
erikaheidi
56
14k
Building Applications with DynamoDB
mza
96
6.8k
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
286
14k
Facilitating Awesome Meetings
lara
57
6.7k
Unsuck your backbone
ammeep
671
58k
[RailsConf 2023] Rails as a piece of cake
palkan
58
6.2k
Build The Right Thing And Hit Your Dates
maggiecrowley
38
3k
YesSQL, Process and Tooling at Scale
rocio
174
15k
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
162
16k
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
21
1.3k
The Straight Up "How To Draw Better" Workshop
denniskardys
239
140k
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
659
61k
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