💪🏻Activityを改善した話 at DroidKaigi 2017

💪🏻Activityを改善した話 at DroidKaigi 2017

DroidKaigi2017

TwitterID変えました。@lvla0805 -> @MoyuruAizawa

5f533179da1c82722252cbcb93e7356f?s=128

Moyuru Aizawa

March 10, 2017
Tweet

Transcript

  1. !ActivityΛվળͨ͠࿩ @lvla0805

  2. MWMB MWMB Ѫᖒ๖ (Moyuru Aizawa) - Lead Kotlin engineer of

    Pairs Div. Eureka, Inc.
  3. - Pairs - Couples - a member of IAC/Match Group

  4. Pairs

  5. Pairs

  6. ·ͣ͸ཧ૝ͷ͓૬खΛ୳͠·͢

  7. ؾʹͳΔ͓૬खʹ͍͍Ͷ!ΛૹΔ

  8. Ϛονϯάޙɺϝοηʔδަ׵

  9. αϯϓϧίʔυʹؔͯ͠

  10. Pairs͸Java/Kotlin͕ࠞࡏ͍ͯ͠·͢ɻ Αͬͯɺαϯϓϧίʔυʹ͓͍ͯ΋྆ ݴޠ͕ར༻͞Ε͍ͯ·͢ɻ

  11. Kotlin͕ۤखͳϑϨϯζ ͷΈͳ͞Μ

  12. ͱ͠ΐ͔ΜͰKotlinʹ͍ͭ ͯ͠Β΂·͠ΐʔ!

  13. Instanceੜ੒ final Serval serval = new Serval(); Kaban kaban =

    new Kaban(); val serval = Serval() var kaban = Kaban()
  14. Instanceੜ੒ final Serval serval = new Serval(); Kaban kaban =

    new Kaban(); val serval = Serval() var kaban = Kaban()
  15. Instanceੜ੒ final Serval serval = new Serval(); Kaban kaban =

    new Kaban(); val serval = Serval() var kaban = Kaban()
  16. Instanceੜ੒ final Serval serval = new Serval(); Kaban kaban =

    new Kaban(); val serval = Serval() var kaban = Kaban()
  17. 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) Έ͔͡ʔ͍
  18. 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) Έ͔͡ʔ͍
  19. 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) Έ͔͡ʔ͍
  20. 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() }
  21. 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() }
  22. 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() }
  23. 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() }
  24. ຊ୊

  25. Pairsʹ͸ !Activity͕͍ͬͺ͍ ͩͬͨ  ͢͝ʔ͍

  26. ར༻ٕज़ʹ͍ͭͯ

  27. ٕज़֓ཁ Languages Java/Kotlin Network Retrofit2, OkHttp3 O/R Mapper Orma Reactive

    Programming RxJava1 Dependency Injection Dagger2
  28. !Activity?

  29. ‣ ϏδωεϩδοΫΛ͍࣋ͬͯΔ ‣ IOॲཧ͕͕ͬͭΓॻ͔Ε͍ͯΔ ‣ ͱʹ͔͘ߦ਺͕ଟ͍ !

  30. ϏδωεϩδοΫ

  31. ϏδωεϩδοΫ if (isPaidUser) { // ༗ྉձһͷΈѻ͑ΔػೳΛ༗ޮԽ͢Δ } else { //

    ༗ྉձһͷΈѻ͑ΔػೳΛແޮԽ͢Δ }
  32. ϏδωεϩδοΫ if (isLikedByPartner) { // ͓૬ख͔Β͍͍Ͷ!Λ΋Β͍ͬͯΔঢ়ଶ } else if (isLikedByMe)

    { // ͍͍Ͷ!Λ͍ͯ͠Δঢ়ଶ } else if (isLikedWithMessage) { // ϝοηʔδ෇͖͍͍Ͷ!Λ͍ͯ͠Δঢ়ଶ } else if (isLooked) { // ΈͯͶ!Λ͍ͯ͠Δঢ়ଶ } else if (isLookedWithMessage) { // ϝοηʔδ෇͖ΈͯͶ!Λ͍ͯ͠Δঢ়ଶ } else if (isHidden) { // ඇදࣔઃఆΛͨ͠௚ޙ } else if (isBlocked) { // ϒϩοΫઃఆΛͨ͠௚ޙ } else if (...) { ... }
  33. ‣ ͜Ε͚ͩͳΒ໰୊͸খͦ͞͏ʹΈ͑Δ͕… ‣ ϏδωεϩδοΫ͕ॏͳΓ߹ͬͯɺΞϓϦέʔγϣ ϯ͕࡞ΒΕΔ ‣ ෳ਺ͷϏδωεϩδοΫ͕Activity/Fragment/Adapter ʹ͋;Εͯ͘ΔͱɺίʔυΛ௥͏ͷ͕ࠔ೉ʹ… ϏδωεϩδοΫ

  34. IO

  35. IO userClient.fetchMathingUsers() .subscribeOnIo() .doOnNex { users -> userDao.insertAll(users) } .onError

    { userDao.findAll() } .observeOnMain() .subscribe( { users -> showUsers(users) }, { ... })
  36. ͱʹ͔͘ߦ਺͕ଟ͍ɺͭΒ͍

  37. ఢ͸!ActivityͷΈͳΒͣ

  38. ‣ ΊͪΌͪ͘Όͳ੹຿ ‣ ਆΫϥε ‣ static͓͡͞Μ ͭΒΈ

  39. ΊͪΌͪ͘Όͳ੹຿

  40. ‣ Converter ‣ JsonΛϚοϐϯάͨ͠Ϋϥε͔ΒɺΞϓϦ಺Ͱ࢖ ͍΍͍͢σʔλΫϥεʹม׵Λߦ͏Ϋϥε ‣ ม׵Λߦ͏͜ͱͷΈ͕੹຿ͷ͸ͣ ΊͪΌͪ͘Όͳ੹຿ class UserConverter

    { fun convert(res: MatchingUsersResponse): List<User> { return res.users.map { resUser -> User(resUser.name, …) } } }
  41. ͳͷ͕ͩ….

  42. ‣ Convert͢ΔաఔͰϢʔβʔͷϑΟϧλϦϯάΛߦ͏ Converter ΊͪΌͪ͘Όͳ੹຿ class UserConverter { fun convert(res: MatchingUsersResponse):

    List<User> { return res.users.map { user -> User(user.name, user.age, ...) } .filter { … } } }
  43. None
  44. ਆΫϥε

  45. XxxManager ਆΫϥε

  46. ‣ SearchManager ‣ ݕࡧ৚݅ͷอ࣋ ‣ ݕࡧ݁Ռͷอ࣋ ‣ ݕࡧ݁ՌΛऔಘ͢Δॲཧͷ؅ཧ ‣ …

    ਆΫϥε
  47. ‣ ਆʹک͑ ‣ ਆʹفΓ ‣ ਆʹ໋Λ๋͛Δ೔ʑ ਆΫϥε

  48. Static͓͡͞Μ

  49. ‣ API΍DatabaseΛૢ࡞͢ΔϝιουͳͲɺ͍ͨΔͱ͜ ΖͰstaticϝιουΛར༻࣮ͨ͠૷ʹͳ͍ͬͯͨɻ static͓͡͞Μ

  50. static͓͡͞Μ public static Observable<List<User>> fetchMatchingUsers() { return PairsClient.getRetrofit() .create(MatchingUsersService.class) .fetch()

    ... }
  51. static͓͡͞Μ public static Observable<List<User>> fetchMatchingUsers() { return PairsClient.getRetrofit() .create(MatchingUsersService.class) .fetch()

    ... }
  52. static͓͡͞Μ public static Observable<List<User>> fetchMatchingUsers() { return PairsClient.getRetrofit() .create(MatchingUsersService.class) .fetch()

    ... }
  53. static͓͡͞Μ public static Observable<List<User>> fetchMatchingUsers() { return PairsClient.getRetrofit() .create(MatchingUsersService.class) .fetch()

    ... }
  54. ‣ ύϑΥʔϚϯε͕ΑΖ͘͠ͳ͍ ‣ “staticϝιου಺ͰstaticϝιουΛ࢖ͬͯRetrofitΛ औಘ͠ɺInterfaceͷ࣮૷ͷੜ੒Λߦ͏”ͱ͍͏ӅΕͨ ೖྗ஋͕͋Δɻ ‣ ӅΕ͍ͯͯ͸ϞοΫ͕Ͱ͖ͳ͍ ‣ UnitςετΉ͔͍ͣ͠…

    static͓͡͞Μ
  55. static͓͡͞Μ class MatchingUsersClient( private val service: MatchingUsersService) { fun fetch():

    Observable<List<User>> { return service.fetch()..... } }
  56. ΍͹͍!Ͳ͏ʹ͔͠ͳ͍ͱ!

  57. EMERGENCY!!

  58. ‣ Retrofit1->2΁ͷҠߦ࣌ ‣ ຊདྷPOST͢΂͖ͱ͜ΖΛGETʹ͍ͯͨ͠ ‣ ॏཁͳػೳ͕࢖͑ͳ͍ঢ়ଶʹ Emergency

  59. ‣ ࠓޙಉࣦ͡ഊΛ܁Γฦ͞ͳ͍ͨΊʹ͸? ‣ खಈͷςετ૿΍͢ͷ͸ҙຯ͕ແ͍ΑͶ ‣ ςετίʔυॻ͍ͯࣗಈԽ͍ͨ͠ΑͶɻ Emergency

  60. ςετίʔυ
 ॻ͖·͠ΐ͏!

  61. ॻ͚ͳ͘ͳ͍?

  62. ‣ ӅΕͨೖྗ஋΍ग़ྗ஋ ‣ ؔΘΓ߹͏staticϝιου ‣ DBʹґଘ͍ͯ͠ΔClient ‣ ෳ਺ͷ໾ׂΛ࣋ͪɺ༷ʑͳঢ়ଶΛ࣋ͭਆʑ ‣ Activityʹॻ͔ΕͨϏδωεϩδοΫୡ

    ςετͰ͖ͳ͍ΑͶ
  63. ‣ ੹຿ͷ෼ׂ ‣ !Activityͷվળ ‣ ϏδωεϩδοΫͷഉআ ‣ IOͷഉআ ‣ Staticϝιου܈ͱਆʑʹҾୀ͍ͯͨͩ͘͠

    ՝୊
  64. େʑతϦϑΝΫλϦϯά

  65. Before Entity/Dao Database API Client Activity / Fragment

  66. ‣ ੹຿͝ͱʹϨΠϠʔΛ෼͚Δ ‣ Retrofit͕ੜ੒͢ΔInterfaceͷ࣮૷Λհͯ͠ɺServerͱ΍ΓऔΓ Λߦ͏Client ‣ Databaseͱ΍ΓऔΓΛߦ͏Dao ‣ σʔλιʔεΛӅṭ͠ɺσʔλͷอଘ/औಘΛߦ͏Repository ‣

    ϏδωεϩδοΫΛ࣮ݱ͢ΔUseCase ‣ UseCaseͱViewΛͭͳ͙Presenter ‣ ֤ϨΠϠʔ͸ObservableΛհͯ͠΍ΓऔΓΛߦ͏ ੹຿ͷ෼ׂ
  67. After Dao Database API Client Repository UseCase Presenter Activity /

    Fragment Service
  68. After Dao Client Repository UseCase Presenter Activity / Fragment Retrofit͕࣮૷͢ΔInterface

    Database API Service
  69. After Dao Client Repository UseCase Presenter Activity / Fragment Serverͱͷσʔλૹड৴

    Database API Service
  70. After Dao Client Repository UseCase Presenter Activity / Fragment DBͱͷσʔλૹड৴

    Database API Service
  71. After Dao Repository UseCase Presenter Activity / Fragment Client σʔλऔಘ/อଘ

    Database API Service
  72. After Dao Repository UseCase Presenter Activity / Fragment Client ϏδωεϩδοΫ

    Database API Service
  73. After Dao Repository UseCase Presenter Activity / Fragment Client ϏδωεϩδοΫͷར༻


    Activity/Fragmentͷૢ࡞ Database API Service
  74. After Dao Repository UseCase Presenter Activity / Fragment Client Viewͷૢ࡞


    Activityґଘͷػೳͷૢ࡞ Database API Service
  75. After Dao Client Repository UseCase Presenter Activity / Fragment Observable

    Observable Observable Observable Observable Observable Database API Service
  76. Matching

  77. Client Dao Client Repository UseCase Presenter Activity / Fragment Database

    API Service
  78. ‣ Retrofitʹੜ੒ͤͨ͞Interfaceͷ࣮૷Λར༻ͯ͠௨৴Λ ߦ͏ ‣ JsonͷϚοϐϯάΫϥεΛΞϓϦ಺Ͱར༻͠΍͍͢ σʔλΫϥεʹม׵͢Δ Client

  79. Client class UserClient(private val service: UserService) { fun fetchMatchingUsers(offset: Int):Observable<List<User>>

    { return service.fetchMatchingUsers(offset) .map { response -> UserConverter.convert(response) } } }
  80. Client class UserClient(private val service: UserService) { fun fetchMatchingUsers(offset: Int):Observable<List<User>>

    { return service.fetchMatchingUsers(offset) .map { response -> UserConverter.convert(response) } } }
  81. Client class UserClient(private val service: UserService) { fun fetchMatchingUsers(offset: Int):Observable<List<User>>

    { return service.fetchMatchingUsers(offset) .map { response -> UserConverter.convert(response) } } }
  82. Client Test val userService = mock<UserService>() val client = UserClient(userService)

    userService.invoked.thenReturn(...) client.fetchMathingUsers().test().run { ... }
  83. Dao Dao Client Repository UseCase Presenter Activity / Fragment Database

    API Service
  84. ‣ OrmaDatabaseΛར༻ͯ͠Database͔Βσʔλͷऔಘ Λߦ͏ ‣ OrmaDatabaseΛར༻ͯ͠Database΁σʔλΛอଘ͢ Δ Dao

  85. 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() } }
  86. 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() } }
  87. 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() } }
  88. 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() } }
  89. Dao Test val dao = UserDao(orma) dao.insert(...) orma.selectFromUser() .executeAsObservable() .toList()

    .test() .run { ... } orma.insertIntoUser()… dao.findAll().test().run { ... }
  90. Repository Dao Repository UseCase Presenter Activity / Fragment Client Database

    API Service
  91. ‣ ClientͱDaoͷͲͪΒ͔͔ΒσʔλͷऔಘΛߦ͏ ‣ Clientܦ༝ͰAPI͔ΒσʔλͷऔಘΛߦͬͨ৔߹ʹ͸ DatabaseʹσʔλΛอଘ͢Δ Repository

  92. 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() } } }
  93. 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() } } }
  94. 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() } } }
  95. 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() } } }
  96. 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() } } }
  97. 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() } } }
  98. 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() } } }
  99. UseCase Dao Repository UseCase Presenter Activity / Fragment Client Database

    API Service
  100. ‣ ϏδωεϩδοΫΛ࣮ݱ͢Δ UseCase

  101. 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 } } } }
  102. 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 } } } }
  103. 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 } } } }
  104. 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 } } } }
  105. 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 } } } }
  106. 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 } } } }
  107. 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 } } } }
  108. Presenter Dao Repository UseCase Presenter Activity / Fragment Client Database

    API Service
  109. ‣ UseCaseΛར༻ͯ͠ϏδωεϩδοΫΛ࣮ݱ͢Δ ‣ ϏδωεϩδοΫͷ൓өΛActivity/Fragmentʹରͯ͠ ߦ͏ ‣ Activity/Fragmentͱ͸Presenterͷఏڙ͢ΔContractΠ ϯλʔϑΣʔεΛհͯ͠ߦ͏ Presenter

  110. 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() } }
  111. 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() } }
  112. 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() } }
  113. 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() } }
  114. 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() } }
  115. 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() } }
  116. 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() } }
  117. 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() } }
  118. Activity,
 Fragment Dao Repository UseCase Presenter Activity / Fragment Client

    Database API Service
  119. ‣ Viewͷૢ࡞ ‣ Activityʹґଘͨ͠ػೳͷϋϯυϦϯά ‣ ͜ΕΒΛPresenterͷ࣋ͭContractΠϯλʔϑΣʔεΛ հͯ͠ఏڙ͢Δ Activity/Fragment

  120. 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() } }
  121. 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() } }
  122. 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() } }
  123. Dependency Injection

  124. ґଘؔ܎Graph Dao Client Repository UseCase Presenter Activity / Fragment Orma

    Context Service Retrofit OkHttp
  125. ‣ Ϋϥε಺Ͱґଘ͢ΔΫϥεͷΠϯελϯεੜ੒Λߦ͏ ͷ͸ྑ͘ͳ͍ ‣ ςετ࣌ʹ੾Γସ͑Δͱ͍ͬͨࣄ͕Ͱ͖ͳ͍ ‣ ֎Ͱੜ੒͠ɺίϯετϥΫλ·ͨ͸ϝϯόʔʹରͯ͠ ґଘੑΛ͍ΕΔ ‣ ґଘੑΛ؅ཧ͢ΔΫϥεΛ࡞Δ

    ‣ ґଘੑΛ؅ཧ͢ΔΫϥεͷ࣮૷/ϝϯς͕େม ‣ DIΛ࢖͓͏ -> Dagger2 DI
  126. Α͠!
 ͜Μͳײ͡Ͱߦ͜͏!

  127. ‣ Pairs JP AndroidνʔϜ͸౰࣌3ਓ ‣ 2ਓ͸ͦͷ··ࢪࡦͷ࣮૷ ‣ ࢲ͕୲౰ ‣ Pairs

    Globalͷ@yuyakaidoʹ΋ख఻ͬͯ΋Β͏ ‣ ܭ 1.5ਓ͘Β͍Ͱ࣮૷ Ͳ͏ਐΊ͍ͯͬͨͷ͔
  128. ‣ ϝΠϯετϦʔϜͱ͸ผʹrefactorϒϥϯν ‣ ϝΠϯετϦʔϜͱͷίϯϑϦΫτΛආ͚ΔͨΊʹ Activity/Fragment΁ͷมߋ͸ඞཁ࠷௿ݶʹ཈͑ͨ ‣ PresenterΛ࣮૷͠ɺActivity/FragmentʹContractΠ ϯλʔϑΣʔεΛ࣮૷͠Α͏ͱࢥ͏ͱɺมߋ͕๲ େʹͳΔͷͰPresenterͷ࣮૷͸ޙճ͠ʹ ‣

    Activity/Fragment͸Ұ୴UseCaseΛ௚Ͱѻ͏͜ͱΛ ڐ༰ Ͳ͏ਐΊ͍ͯͬͨͷ͔
  129. Ͳ͏ਐΊ͍ͯͬͨͷ͔ ‣ ґଘؔ܎Graphͷϧʔτ͔Βมߋ ΛՃ͍͑ͯ͘ ‣ ·ͣ͸Dagger2ͷModule/ ComponentΛ࡞੒ɻ ‣ OkHttp, Retrofit,

    Ormaͷprovideϝ ιουΛ࣮૷
  130. ‣ σʔλιʔε͝ͱʹDao, Client, RepositoryΛ࣮૷ ‣ UserDao, UserClient, UserRepository ‣ MeDao,

    MeClient, MeRepository ‣ … ‣ σʔλιʔε͝ͱʹɺRepository͕࣮૷׬ྃ࣍ୈ UseCaseΛ࣮૷ ‣ Activity/FragmentʹมߋΛՃ͑Δ Ͳ͏ਐΊ͍ͯͬͨͷ͔ ‘͓૬ख’ͷσʔλΫϥε ‘ࣗ෼’ͷσʔλΫϥε
  131. ‣ ͍͖ͳΓΰʔϧΛ໨ࢦ͞ͳ͍ ‣ ग़དྷΔݶΓਖ਼ৗಈ࡞͢Δঢ়ଶΛอͭ ‣ খ͍͞มߋΛੵΈॏͶ͍ͯ͘ ‣ ΠϯλʔϑΣʔε͕มΘΔͱݺͼग़͠ݩͷมߋ΋ඞ ཁʹͳΔͷͰɺҰ୴ճΓಓͯ͠໨తΛୡ੒͢Δ ‣

    ͦͷޙʹΠϯλʔϑΣʔεΛमਖ਼ͯ͠ݺͼग़͠ݩ΁ ͷมߋΛߦ͍ͬͯ͘ େ੾ͳ͜ͱ
  132. ‣ ৽͍͠ઃܭʹؔͯ͠νʔϜϝϯόʔ͕ཧղͰ͖Δ·Ͱ ͬ͘͡Γͱڭ͑Δ ‣ ͍ͭͰ΋ಡΈฦͤΔΑ͏ʹɺQiita:teamͳͲʹ·ͱΊ ͓ͯ͘ େ੾ͳ͜ͱ

  133. 1.5ϲ݄ޙ…

  134. Release

  135. վળޙ

  136. ‣ ςετίʔυ͕֨ஈʹॻ͖΍͘͢ͳͬͨ ‣ શମͷݟ௨͕͠ྑ͘ͳͬͨ ‣ ԿॲʹɺԿΛॻ͚͹Α͍ͷ͔͕෼͔Γ΍͍͢ ‣ ԿॲʹɺԿ͕͋Δͷ͔͕෼͔Γ΍͍͢ ‣ ϥΠϒϥϦͷࡌͤସ͕͑༰қʹͳΓͦ͏

    ‣ e.g. ORMΛࡌͤସ͍͑ͨͱͳͬͨΒDao΁ͷมߋ ͚ͩͰ׬݁͢Δ ͜ͷ࣮૷ͷϝϦοτ
  137. ‣ Ϋϥε਺ɺϝιου਺͕ଟ͍ ‣ Dex Countࢯʙ ‣ ࣮૷ίετ ‣ ֶशίετ ͜ͷ࣮૷ͷσϝϦοτ

  138. Conclusion

  139. ‣ Dao, Client, Repository, UseCaseͳͲ੹຿͝ͱʹϨΠ ϠʔΛ෼͚ͨ ‣ ModelͱViewͷؒʹPresenterΛઃ͚ͯViewͱModelΛ ໌֬ʹ෼͚Δ ‣

    Dagger2ʹґଘੑͷղܾΛҕৡ͢Δ ‣ σʔλιʔε͝ͱʹԼͷϨΠϠʔ͔Βॱʹ࣮૷͍ͯ͠ ͘ ‣ ϝϯόʔ΁ͷϨΫνϟʔ͸͔ܽ͞ͳ͍ ‣ จͱͯ͠࢒͠ɺ͍ͭͰ΋ݟฦ͢Α͏ʹͨ͠ ·ͱΊ
  140. Thank you