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
35
graphicsLayer
lvla
0
170
BluetoothDevice.getName()に裏切られた話
lvla
0
260
Jetpack Composeで画像クロップ機能を実装する
lvla
0
950
Jetpack Compose drag gesture and pinch gesture
lvla
1
3.2k
Jetpack Compose Layout API
lvla
1
610
BLEを使ったアプリを継続的に開発するために
lvla
0
910
RecyclerView.ItemAnimator
lvla
1
290
RecycledViewPool
lvla
1
170
Other Decks in Programming
See All in Programming
Seamless Flutter Native Integration: FFI & Pigeon - Dreamwalker (JaichangPark / 박제창) @FlutterKaigi2024
itsmedreamwalker
0
120
アニメーションを最深まで理解してパフォーマンスを向上させる
mine2424
0
110
Vapor Revolution
kazupon
2
2.6k
我々のデザインシステムは Chakra v3 にアップデートします
shunya078
2
3k
rails statsで大解剖 🔍 “B/43流” のRailsの育て方を歴史とともに振り返ります
shoheimitani
2
720
Missing parts when designing and implementing Android UI
ericksli
0
390
tidymodelsによるtidyな生存時間解析 / Japan.R2024
dropout009
1
610
useSyncExternalStoreを使いまくる
ssssota
4
820
ブラウザ単体でmp4書き出すまで - muddy-web - 2024-12
yue4u
0
130
フロントエンドのディレクトリ構成どうしてる? Feature-Sliced Design 導入体験談
osakatechlab
8
3.8k
StarlingMonkeyを触ってみた話 - 2024冬
syumai
2
220
クリエイティブコーディングとRuby学習 / Creative Coding and Learning Ruby
chobishiba
0
3.7k
Featured
See All Featured
Put a Button on it: Removing Barriers to Going Fast.
kastner
59
3.6k
What’s in a name? Adding method to the madness
productmarketing
PRO
22
3.2k
Scaling GitHub
holman
458
140k
Java REST API Framework Comparison - PWX 2021
mraible
PRO
28
8.3k
Reflections from 52 weeks, 52 projects
jeffersonlam
346
20k
Automating Front-end Workflow
addyosmani
1366
200k
KATA
mclloyd
29
14k
Optimising Largest Contentful Paint
csswizardry
33
3k
Writing Fast Ruby
sferik
627
61k
Documentation Writing (for coders)
carmenintech
65
4.5k
The World Runs on Bad Software
bkeepers
PRO
65
11k
YesSQL, Process and Tooling at Scale
rocio
169
14k
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