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

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

DroidKaigi2017

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

5f533179da1c82722252cbcb93e7356f?s=128

Moyuru Aizawa

March 10, 2017
Tweet

Transcript

  1. 4.
  2. 5.
  3. 13.

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

    new Kaban(); val serval = Serval() var kaban = Kaban()
  4. 14.

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

    new Kaban(); val serval = Serval() var kaban = Kaban()
  5. 15.

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

    new Kaban(); val serval = Serval() var kaban = Kaban()
  6. 16.

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

    new Kaban(); val serval = Serval() var kaban = Kaban()
  7. 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) Έ͔͡ʔ͍
  8. 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) Έ͔͡ʔ͍
  9. 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) Έ͔͡ʔ͍
  10. 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() }
  11. 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() }
  12. 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() }
  13. 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() }
  14. 24.
  15. 32.

    ϏδωεϩδοΫ if (isLikedByPartner) { // ͓૬ख͔Β͍͍Ͷ!Λ΋Β͍ͬͯΔঢ়ଶ } else if (isLikedByMe)

    { // ͍͍Ͷ!Λ͍ͯ͠Δঢ়ଶ } else if (isLikedWithMessage) { // ϝοηʔδ෇͖͍͍Ͷ!Λ͍ͯ͠Δঢ়ଶ } else if (isLooked) { // ΈͯͶ!Λ͍ͯ͠Δঢ়ଶ } else if (isLookedWithMessage) { // ϝοηʔδ෇͖ΈͯͶ!Λ͍ͯ͠Δঢ়ଶ } else if (isHidden) { // ඇදࣔઃఆΛͨ͠௚ޙ } else if (isBlocked) { // ϒϩοΫઃఆΛͨ͠௚ޙ } else if (...) { ... }
  16. 34.

    IO

  17. 35.

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

    { userDao.findAll() } .observeOnMain() .subscribe( { users -> showUsers(users) }, { ... })
  18. 40.

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

    { fun convert(res: MatchingUsersResponse): List<User> { return res.users.map { resUser -> User(resUser.name, …) } } }
  19. 42.

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

    List<User> { return res.users.map { user -> User(user.name, user.age, ...) } .filter { … } } }
  20. 43.
  21. 44.
  22. 61.

  23. 75.
  24. 76.

    After Dao Client Repository UseCase Presenter Activity / Fragment Observable

    Observable Observable Observable Observable Observable Database API Service
  25. 77.
  26. 80.

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

    { return service.fetchMatchingUsers(offset) .map { response -> UserConverter.convert(response) } } }
  27. 81.

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

    { return service.fetchMatchingUsers(offset) .map { response -> UserConverter.convert(response) } } }
  28. 82.

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

    { return service.fetchMatchingUsers(offset) .map { response -> UserConverter.convert(response) } } }
  29. 83.

    Client Test val userService = mock<UserService>() val client = UserClient(userService)

    userService.invoked.thenReturn(...) client.fetchMathingUsers().test().run { ... }
  30. 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() } }
  31. 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() } }
  32. 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() } }
  33. 89.

    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() } }
  34. 90.

    Dao Test val dao = UserDao(orma) dao.insert(...) orma.selectFromUser() .executeAsObservable() .toList()

    .test() .run { ... } orma.insertIntoUser()… dao.findAll().test().run { ... }
  35. 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() } } }
  36. 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() } } }
  37. 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() } } }
  38. 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() } } }
  39. 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() } } }
  40. 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() } } }
  41. 99.

    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() } } }
  42. 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 } } } }
  43. 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 } } } }
  44. 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 } } } }
  45. 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.ONLY_USER } else { FacebookPublicSetting.NEITHER } } } }
  46. 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.ONLYE_USER } else { FacebookPublicSetting.NEITHER } } } }
  47. 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 } } } }
  48. 108.

    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 } } } }
  49. 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() } }
  50. 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() } }
  51. 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() } }
  52. 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() } }
  53. 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() } }
  54. 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() } }
  55. 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() } }
  56. 118.

    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() } }
  57. 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() } }
  58. 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() } }
  59. 123.

    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() } }
  60. 128.

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

    Globalͷ@yuyakaidoʹ΋ख఻ͬͯ΋Β͏ ‣ ܭ 1.5ਓ͘Β͍Ͱ࣮૷ Ͳ͏ਐΊ͍ͯͬͨͷ͔
  61. 131.

    ‣ σʔλιʔε͝ͱʹDao, Client, RepositoryΛ࣮૷ ‣ UserDao, UserClient, UserRepository ‣ MeDao,

    MeClient, MeRepository ‣ … ‣ σʔλιʔε͝ͱʹɺRepository͕࣮૷׬ྃ࣍ୈ UseCaseΛ࣮૷ ‣ Activity/FragmentʹมߋΛՃ͑Δ Ͳ͏ਐΊ͍ͯͬͨͷ͔ ‘͓૬ख’ͷσʔλΫϥε ‘ࣗ෼’ͷσʔλΫϥε
  62. 135.
  63. 136.
  64. 139.
  65. 140.

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

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