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
graphicsLayer
lvla
0
140
BluetoothDevice.getName()に裏切られた話
lvla
0
180
Jetpack Composeで画像クロップ機能を実装する
lvla
0
810
Jetpack Compose drag gesture and pinch gesture
lvla
1
2.6k
Jetpack Compose Layout API
lvla
1
570
BLEを使ったアプリを継続的に開発するために
lvla
0
840
RecyclerView.ItemAnimator
lvla
1
260
RecycledViewPool
lvla
1
140
CameraX
lvla
2
2.2k
Other Decks in Programming
See All in Programming
DynamoDB コスト最適化っぽいことの基本 with Terraform
kuro_kurorrr
2
250
なぜ宣言的 UI は壊れにくいのか / Why declarative UI is less fragile
uenitty
29
13k
Product Management LT会_クアンド新家
shinshin
0
210
GraphQL はいいぞ! ~Laravel で学ぶ GraphQL 入門~
azuki
1
160
Namespace on read
tagomoris
2
370
しくじり先生 Image Matching Challenge 2024 編
goosehaaan
0
810
CSC307 Lecture 13
javiergs
PRO
0
150
ぼっちを避けて楽しむためのアノテコノテ / Various Tips and Tricks to Avoid Loneliness and Have Fun
nrslib
3
1.7k
大規模マルチテナントを解決するYugabyteDBという選択肢
nnaka2992
1
250
スクラムマスターって孤独じゃないですか?
yoshitaroyoyo
1
140
AWS CDKにおける「再利用性」を考える / aws-cdk-reusability
gotok365
6
1.3k
DDDを志して3年経ったら「DDDの皮を被ったクリーンアーキテクチャ」になった話【デブサミ2024夏】
texmeijin
1
620
Featured
See All Featured
The Brand Is Dead. Long Live the Brand.
mthomps
52
36k
A Tale of Four Properties
chriscoyier
155
22k
Become a Pro
speakerdeck
PRO
15
4.8k
Being A Developer After 40
akosma
72
580k
How GitHub (no longer) Works
holman
305
140k
Testing 201, or: Great Expectations
jmmastey
33
6.9k
Making Projects Easy
brettharned
111
5.7k
Build your cross-platform service in a week with App Engine
jlugia
227
17k
Rails Girls Zürich Keynote
gr2m
93
13k
Navigating Team Friction
lara
181
13k
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
13
430
Side Projects
sachag
451
42k
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