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
96
graphicsLayer
lvla
0
210
BluetoothDevice.getName()に裏切られた話
lvla
0
350
Jetpack Composeで画像クロップ機能を実装する
lvla
0
1.2k
Jetpack Compose drag gesture and pinch gesture
lvla
1
3.9k
Jetpack Compose Layout API
lvla
1
660
BLEを使ったアプリを継続的に開発するために
lvla
0
1k
RecyclerView.ItemAnimator
lvla
1
320
RecycledViewPool
lvla
1
220
Other Decks in Programming
See All in Programming
Gemini CLIの"強み"を知る! Gemini CLIとClaude Codeを比較してみた!
kotahisafuru
3
900
Dart 参戦!!静的型付き言語界の隠れた実力者
kno3a87
0
160
Vibe coding コードレビュー
kinopeee
0
400
抽象化という思考のツール - 理解と活用 - / Abstraction-as-a-Tool-for-Thinking
shin1x1
1
920
バイブスあるコーディングで ~PHP~ 便利ツールをつくるプラクティス
uzulla
1
320
それ CLI フレームワークがなくてもできるよ / Building CLI Tools Without Frameworks
orgachem
PRO
17
3.6k
なぜ今、Terraformの本を書いたのか? - 著者陣に聞く!『Terraformではじめる実践IaC』登壇資料
fufuhu
3
370
AWS Summit Japan 2024と2025の比較/はじめてのKiro、今あなたは岐路に立つ
satoshi256kbyte
1
260
#QiitaBash TDDで(自分の)開発がどう変わったか
ryosukedtomita
1
350
No Install CMS戦略 〜 5年先を見据えたフロントエンド開発を考える / no_install_cms
rdlabo
0
420
MCP連携で加速するAI駆動開発/mcp integration accelerates ai-driven-development
bpstudy
0
250
TypeScriptでDXを上げろ! Hono編
yusukebe
4
920
Featured
See All Featured
Side Projects
sachag
455
43k
Why You Should Never Use an ORM
jnunemaker
PRO
58
9.5k
Reflections from 52 weeks, 52 projects
jeffersonlam
351
21k
[RailsConf 2023] Rails as a piece of cake
palkan
56
5.7k
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
48
2.9k
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
18
1k
Fashionably flexible responsive web design (full day workshop)
malarkey
407
66k
Product Roadmaps are Hard
iamctodd
PRO
54
11k
Why Our Code Smells
bkeepers
PRO
337
57k
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
251
21k
Building Better People: How to give real-time feedback that sticks.
wjessup
367
19k
How to Create Impact in a Changing Tech Landscape [PerfNow 2023]
tammyeverts
53
2.9k
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