Upgrade to Pro — share decks privately, control downloads, hide ads and more …

droidkaigi-2019

Yuya Kaido
February 07, 2019

 droidkaigi-2019

Yuya Kaido

February 07, 2019
Tweet

More Decks by Yuya Kaido

Other Decks in Programming

Transcript

  1. ࣗݾ঺հ • ւ౻༏໻ʢ͔͍Ͳ͏Ώ͏΍ʣ • גࣜձࣾΤ΢ϨΧ • AndroidΤϯδχΞ • DroidKaigi •

    2016 - ςετ͓͡͞Μ • 2017 - RxJavaͷΤϥʔϋϯυϦϯά • 2018 - ϚϧνϩάΠϯͷ࣮૷ํ๏ • 2019 - Redux for Android 2 yuyakaido
  2. ໨࣍ • ͸͡Ίʹ • AndroidΞϓϦ։ൃʹ͓͚Δ೰·͍͠໰୊ɺଞͷϓϥοτϑΥʔϜͰͷղܾࡦ • جૅฤ • Reduxͷશମ૾ͱߏ੒ཁૉ •

    αϯϓϧɿTODOΞϓϦ • Ԡ༻ฤ • ඇಉظॲཧɺը໘ભҠɺঢ়ଶมߋͷෛՙɺςετίʔυ • αϯϓϧɿGitHubΫϥΠΞϯτ • ͓ΘΓʹ • ReduxͷϝϦοτɾσϝϦοτ • ଞͷΞʔΩςΫνϟͱͷؔ܎ੑ • ·ͱΊ 4
  3. αϯϓϧ • TodoΞϓϦ • TodoҰཡ • Todo௥Ճ • ٕज़ཁૉ •

    Reduxͷશମ૾ • ֤ཁૉͷ࣮૷ • ঢ়ଶมߋͷखॱ • ϦϙδτϦ • https://github.com/yuyakaido/ReduxKit 18
  4. AppState • ΞϓϦશମͷঢ়ଶΛදݱ͢ΔΫϥε • Data Classͱͯ͠ఆٛ͢Δͱঢ়ଶมߋָ͕ data class AppState( val

    todos: List<Todo> = emptyList() ) : StateType data class Todo( val title: String ) 22
  5. AppAction • ঢ়ଶมߋΛදݱ͢ΔΫϥε • Sealed ClassͰ࣮૷͢Δ͜ͱͰReducerΛγϯϓϧʹͰ͖Δ sealed class AppAction :

    ActionType { data class RefreshTodos(val todos: List<Todo>) : AppAction() data class AddTodo(val todo: Todo) : AppAction() } 24
  6. AppReducer • AppStateͱAppAction͔ΒAppStateΛܭࢉ͢ΔΫϥε • Sealed ClassΛ࢖ͬͨ৔߹͸else͕ෆཁʹͳΔ object AppReducer : ReducerType<AppState,

    AppAction> { override fun reduce(state: AppState, action: AppAction): AppState { return when (action) { is AppAction.RefreshTodos -> { state.copy(todos = action.todos) } is AppAction.AddTodo -> { state.copy(todos = state.todos.plus(action.todo)) } } } } 26
  7. AppStore • AppStateͷอ࣋ɾมߋɾ௨஌Λߦ͏Ϋϥε class AppStore( private val initialState: AppState =

    AppState() ) : StoreType<AppState> { private val state = BehaviorRelay.createDefault(initialState) override fun dispatch(action: ActionType) { state.value?.let { currentState -> val nextState = AppReducer.reduce(currentState, action as AppAction) state.accept(nextState) } } override fun observable(): Observable<AppState> { return state.observeOn(AndroidSchedulers.mainThread()) } } 28
  8. AppStore • AppStateͷอ࣋ɾมߋɾ௨஌Λߦ͏Ϋϥε class AppStore( private val initialState: AppState =

    AppState() ) : StoreType<AppState> { private val state = BehaviorRelay.createDefault(initialState) override fun dispatch(action: ActionType) { state.value?.let { currentState -> val nextState = AppReducer.reduce(currentState, action as AppAction) state.accept(nextState) } } override fun observable(): Observable<AppState> { return state.observeOn(AndroidSchedulers.mainThread()) } } 29 ॳظ஋ͷड͚औΓ
  9. AppStore • AppStateͷอ࣋ɾมߋɾ௨஌Λߦ͏Ϋϥε class AppStore( private val initialState: AppState =

    AppState() ) : StoreType<AppState> { private val state = BehaviorRelay.createDefault(initialState) override fun dispatch(action: ActionType) { state.value?.let { currentState -> val nextState = AppReducer.reduce(currentState, action as AppAction) state.accept(nextState) } } override fun observable(): Observable<AppState> { return state.observeOn(AndroidSchedulers.mainThread()) } } 30 ঢ়ଶͷอ࣋
  10. AppStore • AppStateͷอ࣋ɾมߋɾ௨஌Λߦ͏Ϋϥε class AppStore( private val initialState: AppState =

    AppState() ) : StoreType<AppState> { private val state = BehaviorRelay.createDefault(initialState) override fun dispatch(action: ActionType) { state.value?.let { currentState -> val nextState = AppReducer.reduce(currentState, action as AppAction) state.accept(nextState) } } override fun observable(): Observable<AppState> { return state.observeOn(AndroidSchedulers.mainThread()) } } 31 ঢ়ଶͷมߋ
  11. AppStore • AppStateͷอ࣋ɾมߋɾ௨஌Λߦ͏Ϋϥε class AppStore( private val initialState: AppState =

    AppState() ) : StoreType<AppState> { private val state = BehaviorRelay.createDefault(initialState) override fun dispatch(action: ActionType) { state.value?.let { currentState -> val nextState = AppReducer.reduce(currentState, action as AppAction) state.accept(nextState) } } override fun observable(): Observable<AppState> { return state.observeOn(AndroidSchedulers.mainThread()) } } 32 ঢ়ଶͷ௨஌
  12. View • AppStateΛߪಡ͠ɺTodoҰཡΛඳը͢ΔΫϥε val adapter = TodoAdapter() binding.recyclerView.layoutManager = LinearLayoutManager(this)

    binding.recyclerView.adapter = adapter getAppStore().observable() .subscribeBy { state -> adapter.setTodos(state.todos) adapter.notifyDataSetChanged() } .addTo(disposables) 34
  13. AppAction → AppStore • AppActionΛAppStoreʹ౉͢͜ͱͰঢ়ଶมߋΛߦ͏ val todo = Todo(" NewTask!")

    val action = AppAction.AddTodo(todo) getAppStore().dispatch(action) 39
  14. AppStore → AppReducer • AppStore͸AppReducerΛར༻ͯ࣍͠ͷAppStateΛܭࢉ͢Δ class AppStore( private val initialState:

    AppState = AppState() ) : StoreType<AppState> { private val state = BehaviorRelay.createDefault(initialState) override fun dispatch(action: ActionType) { state.value?.let { currentState -> val nextState = AppReducer.reduce(currentState, action as AppAction) state.accept(nextState) } } } 41
  15. AppStore → AppReducer • AppStore͸AppReducerΛར༻ͯ࣍͠ͷAppStateΛܭࢉ͢Δ class AppStore( private val initialState:

    AppState = AppState() ) : StoreType<AppState> { private val state = BehaviorRelay.createDefault(initialState) override fun dispatch(action: ActionType) { state.value?.let { currentState -> val nextState = AppReducer.reduce(currentState, action as AppAction) state.accept(nextState) } } } 42 Actionͷड͚औΓ
  16. AppStore → AppReducer • AppStore͸AppReducerΛར༻ͯ࣍͠ͷAppStateΛܭࢉ͢Δ class AppStore( private val initialState:

    AppState = AppState() ) : StoreType<AppState> { private val state = BehaviorRelay.createDefault(initialState) override fun dispatch(action: ActionType) { state.value?.let { currentState -> val nextState = AppReducer.reduce(currentState, action as AppAction) state.accept(nextState) } } } 43 ঢ়ଶͷมߋ
  17. AppStore → AppState • AppStore͸ঢ়ଶ͕มߋ͞ΕΔ౓ʹAppStateΛ௨஌͢Δ • AppState͸Viewʹߪಡ͞ΕΔͷͰϝΠϯεϨουͰ௨஌͢Δ class AppStore( private

    val initialState: AppState = AppState() ) : StoreType<AppState> { private val state = BehaviorRelay.createDefault(initialState) override fun observable(): Observable<AppState> { return state.observeOn(AndroidSchedulers.mainThread()) } } 45
  18. AppStore → AppState • AppStore͸ঢ়ଶ͕มߋ͞ΕΔ౓ʹAppStateΛ௨஌͢Δ • AppState͸Viewʹߪಡ͞ΕΔͷͰϝΠϯεϨουͰ௨஌͢Δ class AppStore( private

    val initialState: AppState = AppState() ) : StoreType<AppState> { private val state = BehaviorRelay.createDefault(initialState) override fun observable(): Observable<AppState> { return state.observeOn(AndroidSchedulers.mainThread()) } } 46 ঢ়ଶͷมߋ௨஌
  19. AppState → View • AppStateΛߪಡ͠ɺঢ়ଶมߋΛड͚औΔͱը໘Λߋ৽͢Δ val adapter = TodoAdapter() binding.recyclerView.layoutManager

    = LinearLayoutManager(this) binding.recyclerView.adapter = adapter getAppStore().observable() .subscribeBy { state -> adapter.setTodos(state.todos) adapter.notifyDataSetChanged() } .addTo(disposables) 48
  20. 53

  21. LoggerMiddleware • ϩάग़ྗ͸Middlewareͷ࠷΋୯७ͳར༻ྫ • Dispatch͞ΕͨActionΛஞҰϩάʹग़ྗ͢Δ • ඞཁʹԠͯ͡StateΛग़ྗ͢Δ͜ͱ΋Մೳ class LoggerMiddleware :

    MiddlewareType { override fun before(state: StateType, action: ActionType): Single<ActionType> { Log.d("ReduxKit", "Before dispatching: ${action::class.java.simpleName}") return Single.just(action) } override fun after(state: StateType, action: ActionType): Single<ActionType> { Log.d("ReduxKit", "After dispatching: ${action::class.java.simpleName}") return Single.just(action) } } 54
  22. ThunkMiddleware interface AsyncActionType : ActionType { fun execute(dispatcher: Dispatcher): Single<ActionType>

    } class ThunkMiddleware( private val dispatcher: Dispatcher ) : MiddlewareType { override fun before(state: StateType, action: ActionType): Single<ActionType> { return if (action is AsyncActionType) { action.execute(dispatcher) } else { Single.just(action) } } override fun after(state: StateType, action: ActionType): Single<ActionType> { return Single.just(action) } } 56
  23. ThunkMiddleware interface AsyncActionType : ActionType { fun execute(dispatcher: Dispatcher): Single<ActionType>

    } class ThunkMiddleware( private val dispatcher: Dispatcher ) : MiddlewareType { override fun before(state: StateType, action: ActionType): Single<ActionType> { return if (action is AsyncActionType) { action.execute(dispatcher) } else { Single.just(action) } } override fun after(state: StateType, action: ActionType): Single<ActionType> { return Single.just(action) } } 57 ඇಉظॲཧ༻ͷInterface
  24. ThunkMiddleware interface AsyncActionType : ActionType { fun execute(dispatcher: Dispatcher): Single<ActionType>

    } class ThunkMiddleware( private val dispatcher: Dispatcher ) : MiddlewareType { override fun before(state: StateType, action: ActionType): Single<ActionType> { return if (action is AsyncActionType) { action.execute(dispatcher) } else { Single.just(action) } } override fun after(state: StateType, action: ActionType): Single<ActionType> { return Single.just(action) } } 58 ඇಉظॲཧͷ৔߹͚࣮ͩߦ͢Δ
  25. ThunkMiddleware interface AsyncActionType : ActionType { fun execute(dispatcher: Dispatcher): Single<ActionType>

    } class ThunkMiddleware( private val dispatcher: Dispatcher ) : MiddlewareType { override fun before(state: StateType, action: ActionType): Single<ActionType> { return if (action is AsyncActionType) { action.execute(dispatcher) } else { Single.just(action) } } override fun after(state: StateType, action: ActionType): Single<ActionType> { return Single.just(action) } } 59 Dispatcherͱ͸ʁ
  26. 61

  27. 65

  28. 67

  29. 68

  30. αϯϓϧ • GitHubΫϥΠΞϯτ • ݕࡧը໘ • ελʔҰཡը໘ • ٕज़ཁૉ •

    ඇಉظॲཧ • σʔλͷਖ਼نԽ 69 ݕࡧը໘ ελʔҰཡը໘
  31. AppState • ΞϓϦશମͷঢ়ଶΛදݱ͢ΔΫϥε data class AppState( val domain: DomainState =

    DomainState(), val search: SearchStoreState = SearchStoreState(), val star: StarStoreState = StarStoreState() ) : StateType { fun toSearchViewState(): SearchViewState fun toStarViewState(): StarViewState } 72
  32. AppState • ΞϓϦશମͷঢ়ଶΛදݱ͢ΔΫϥε data class AppState( val domain: DomainState =

    DomainState(), val search: SearchStoreState = SearchStoreState(), val star: StarStoreState = StarStoreState() ) : StateType { fun toSearchViewState(): SearchViewState fun toStarViewState(): StarViewState } 73 υϝΠϯΤϯςΟςΟΛදݱ͢ΔΫϥε
  33. AppState • ΞϓϦશମͷঢ়ଶΛදݱ͢ΔΫϥε data class AppState( val domain: DomainState =

    DomainState(), val search: SearchStoreState = SearchStoreState(), val star: StarStoreState = StarStoreState() ) : StateType { fun toSearchViewState(): SearchViewState fun toStarViewState(): StarViewState } 74 ݕࡧը໘ͷঢ়ଶΛදݱ͢ΔΫϥε
  34. AppState • ΞϓϦશମͷঢ়ଶΛදݱ͢ΔΫϥε data class AppState( val domain: DomainState =

    DomainState(), val search: SearchStoreState = SearchStoreState(), val star: StarStoreState = StarStoreState() ) : StateType { fun toSearchViewState(): SearchViewState fun toStarViewState(): StarViewState } 75 ελʔҰཡը໘ͷঢ়ଶΛදݱ͢ΔΫϥε
  35. 76

  36. AppState data class AppState( val domain: DomainState = DomainState(), val

    search: SearchStoreState = SearchStoreState(), val star: StarStoreState = StarStoreState() ) : StateType { fun toSearchViewState(): SearchViewState { return SearchViewState( isLoading = search.isLoading, repos = search.repos.map { domain.findRepoById(it.id) } ) } fun toStarViewState(): StarViewState { return StarViewState( isLoading = star.isLoading, repos = star.repos.map { domain.findRepoById(it.id) } ) } } 77
  37. AppAction • ঢ়ଶมߋΛදݱ͢ΔΫϥε sealed class AppAction : ActionType { sealed

    class DomainAction : AppAction() sealed class SearchAction : AppAction() sealed class StarAction : AppAction() } 79
  38. AppAction • ঢ়ଶมߋΛදݱ͢ΔΫϥε sealed class AppAction : ActionType { sealed

    class DomainAction : AppAction() sealed class SearchAction : AppAction() sealed class StarAction : AppAction() } 80 DomainStateͷঢ়ଶมߋΛදݱ͢ΔΫϥε
  39. AppAction • ঢ়ଶมߋΛදݱ͢ΔΫϥε sealed class AppAction : ActionType { sealed

    class DomainAction : AppAction() sealed class SearchAction : AppAction() sealed class StarAction : AppAction() } 81 SearchStateͷঢ়ଶมߋΛදݱ͢ΔΫϥε
  40. AppAction • ঢ়ଶมߋΛදݱ͢ΔΫϥε sealed class AppAction : ActionType { sealed

    class DomainAction : AppAction() sealed class SearchAction : AppAction() sealed class StarAction : AppAction() } 82 StarStateͷঢ়ଶมߋΛදݱ͢ΔΫϥε
  41. AppReducer • AppStateͱAppAction͔ΒAppStateΛܭࢉ͢ΔΫϥε object AppReducer : ReducerType<AppState, AppAction> { override

    fun reduce(state: AppState, action: AppAction): AppState { return when (action) { is AppAction.DomainAction -> { state.copy(domain = DomainReducer.reduce(state.domain, action)) } is AppAction.SearchAction -> { state.copy(search = SearchReducer.reduce(state.search, action)) } is AppAction.StarAction -> { state.copy(star = StarReducer.reduce(state.star, action)) } } } } 84
  42. AppReducer • AppStateͱAppAction͔ΒAppStateΛܭࢉ͢ΔΫϥε object AppReducer : ReducerType<AppState, AppAction> { override

    fun reduce(state: AppState, action: AppAction): AppState { return when (action) { is AppAction.DomainAction -> { state.copy(domain = DomainReducer.reduce(state.domain, action)) } is AppAction.SearchAction -> { state.copy(search = SearchReducer.reduce(state.search, action)) } is AppAction.StarAction -> { state.copy(star = StarReducer.reduce(state.star, action)) } } } } 85 DomainReducerΛ࢖༻ͯ͠DomainStateΛܭࢉ
  43. AppReducer • AppStateͱAppAction͔ΒAppStateΛܭࢉ͢ΔΫϥε object AppReducer : ReducerType<AppState, AppAction> { override

    fun reduce(state: AppState, action: AppAction): AppState { return when (action) { is AppAction.DomainAction -> { state.copy(domain = DomainReducer.reduce(state.domain, action)) } is AppAction.SearchAction -> { state.copy(search = SearchReducer.reduce(state.search, action)) } is AppAction.StarAction -> { state.copy(star = StarReducer.reduce(state.star, action)) } } } } 86 SearchReducerΛ࢖༻ͯ͠SearchStateΛܭࢉ
  44. AppReducer • AppStateͱAppAction͔ΒAppStateΛܭࢉ͢ΔΫϥε object AppReducer : ReducerType<AppState, AppAction> { override

    fun reduce(state: AppState, action: AppAction): AppState { return when (action) { is AppAction.DomainAction -> { state.copy(domain = DomainReducer.reduce(state.domain, action)) } is AppAction.SearchAction -> { state.copy(search = SearchReducer.reduce(state.search, action)) } is AppAction.StarAction -> { state.copy(star = StarReducer.reduce(state.star, action)) } } } } 87 StarReducerΛ࢖༻ͯ͠StarStateΛܭࢉ
  45. override fun dispatch(action: ActionType) { state.value?.let { currentState -> Single.just(action)

    .flatMap { originalAction -> var stream = Single.just(originalAction) middlewares.forEach { middleware -> stream = stream.flatMap { currentAction -> middleware.before(currentState, currentAction) } } return@flatMap stream } .doOnSuccess { update(it) } .flatMap { originalAction -> var stream = Single.just(originalAction) middlewares.forEach { middleware -> stream = stream.flatMap { currentAction -> middleware.after(currentState, currentAction) } } return@flatMap stream } .subscribe() } } 91
  46. override fun dispatch(action: ActionType) { state.value?.let { currentState -> Single.just(action)

    .flatMap { originalAction -> var stream = Single.just(originalAction) middlewares.forEach { middleware -> stream = stream.flatMap { currentAction -> middleware.before(currentState, currentAction) } } return@flatMap stream } .doOnSuccess { update(it) } .flatMap { originalAction -> var stream = Single.just(originalAction) middlewares.forEach { middleware -> stream = stream.flatMap { currentAction -> middleware.after(currentState, currentAction) } } return@flatMap stream } .subscribe() } } 92 Dispatch͞ΕͨActionΛى఺ʹॲཧΛ։࢝
  47. override fun dispatch(action: ActionType) { state.value?.let { currentState -> Single.just(action)

    .flatMap { originalAction -> var stream = Single.just(originalAction) middlewares.forEach { middleware -> stream = stream.flatMap { currentAction -> middleware.before(currentState, currentAction) } } return@flatMap stream } .doOnSuccess { update(it) } .flatMap { originalAction -> var stream = Single.just(originalAction) middlewares.forEach { middleware -> stream = stream.flatMap { currentAction -> middleware.after(currentState, currentAction) } } return@flatMap stream } .subscribe() } } 93 ࣄલॲཧ༻ͷMiddlewareΛ࿈݁
  48. override fun dispatch(action: ActionType) { state.value?.let { currentState -> Single.just(action)

    .flatMap { originalAction -> var stream = Single.just(originalAction) middlewares.forEach { middleware -> stream = stream.flatMap { currentAction -> middleware.before(currentState, currentAction) } } return@flatMap stream } .doOnSuccess { update(it) } .flatMap { originalAction -> var stream = Single.just(originalAction) middlewares.forEach { middleware -> stream = stream.flatMap { currentAction -> middleware.after(currentState, currentAction) } } return@flatMap stream } .subscribe() } } 94 Storeͷঢ়ଶΛߋ৽
  49. override fun dispatch(action: ActionType) { state.value?.let { currentState -> Single.just(action)

    .flatMap { originalAction -> var stream = Single.just(originalAction) middlewares.forEach { middleware -> stream = stream.flatMap { currentAction -> middleware.before(currentState, currentAction) } } return@flatMap stream } .doOnSuccess { update(it) } .flatMap { originalAction -> var stream = Single.just(originalAction) middlewares.forEach { middleware -> stream = stream.flatMap { currentAction -> middleware.after(currentState, currentAction) } } return@flatMap stream } .subscribe() } } 95 ࣄޙॲཧ༻ͷMiddlewareΛ࿈݁
  50. Ϣʔεέʔεຖͷ࣮૷ • ϦϙδτϦͷݕࡧॲཧ • ݕࡧը໘ʹೖྗ͞ΕͨΫΤϦΛ΋ͱʹݕࡧΛ࣮ߦ • ݕࡧ݁ՌΛҰཡͰදࣔ • ϦϙδτϦͷελʔॲཧ •

    ϦϙδτϦͷݕࡧ݁Ռ͔ΒελʔॲཧΛ࣮ߦ • ελʔॲཧ׬ྃޙʹɺݕࡧը໘ͱελʔҰཡը໘ʹ൓ө 96
  51. class SearchActionCreator @Inject constructor( private val repository: GitHubRepository ) {

    fun fetchSearchRepositories(query: String): AsyncActionType { return object : AsyncActionType { override fun execute(dispatcher: Dispatcher): Single<ActionType> { return repository.searchRepositoriesByQuery(query) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .doOnSubscribe { dispatcher.dispatch(AppAction.SearchAction.RefreshLoading(true)) } .doOnEvent { _, _ -> dispatcher.dispatch(AppAction.SearchAction.RefreshLoading(false)) } .doOnSuccess { repos -> dispatcher.dispatch(AppAction.DomainAction.PutRepos(repos)) } .map { repos -> AppAction.SearchAction.RefreshRepos(repos) } } } } } 98
  52. class SearchActionCreator @Inject constructor( private val repository: GitHubRepository ) {

    fun fetchSearchRepositories(query: String): AsyncActionType { return object : AsyncActionType { override fun execute(dispatcher: Dispatcher): Single<ActionType> { return repository.searchRepositoriesByQuery(query) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .doOnSubscribe { dispatcher.dispatch(AppAction.SearchAction.RefreshLoading(true)) } .doOnEvent { _, _ -> dispatcher.dispatch(AppAction.SearchAction.RefreshLoading(false)) } .doOnSuccess { repos -> dispatcher.dispatch(AppAction.DomainAction.PutRepos(repos)) } .map { repos -> AppAction.SearchAction.RefreshRepos(repos) } } } } } 99 ݕࡧॲཧΛ࣮ߦ
  53. class SearchActionCreator @Inject constructor( private val repository: GitHubRepository ) {

    fun fetchSearchRepositories(query: String): AsyncActionType { return object : AsyncActionType { override fun execute(dispatcher: Dispatcher): Single<ActionType> { return repository.searchRepositoriesByQuery(query) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .doOnSubscribe { dispatcher.dispatch(AppAction.SearchAction.RefreshLoading(true)) } .doOnEvent { _, _ -> dispatcher.dispatch(AppAction.SearchAction.RefreshLoading(false)) } .doOnSuccess { repos -> dispatcher.dispatch(AppAction.DomainAction.PutRepos(repos)) } .map { repos -> AppAction.SearchAction.RefreshRepos(repos) } } } } } 100 ݕࡧॲཧͷ։࢝࣌ʹϩʔσΟϯάΛදࣔ
  54. class SearchActionCreator @Inject constructor( private val repository: GitHubRepository ) {

    fun fetchSearchRepositories(query: String): AsyncActionType { return object : AsyncActionType { override fun execute(dispatcher: Dispatcher): Single<ActionType> { return repository.searchRepositoriesByQuery(query) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .doOnSubscribe { dispatcher.dispatch(AppAction.SearchAction.RefreshLoading(true)) } .doOnEvent { _, _ -> dispatcher.dispatch(AppAction.SearchAction.RefreshLoading(false)) } .doOnSuccess { repos -> dispatcher.dispatch(AppAction.DomainAction.PutRepos(repos)) } .map { repos -> AppAction.SearchAction.RefreshRepos(repos) } } } } } 101 ݕࡧॲཧͷऴྃ࣌ʹϩʔσΟϯάΛඇදࣔ
  55. class SearchActionCreator @Inject constructor( private val repository: GitHubRepository ) {

    fun fetchSearchRepositories(query: String): AsyncActionType { return object : AsyncActionType { override fun execute(dispatcher: Dispatcher): Single<ActionType> { return repository.searchRepositoriesByQuery(query) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .doOnSubscribe { dispatcher.dispatch(AppAction.SearchAction.RefreshLoading(true)) } .doOnEvent { _, _ -> dispatcher.dispatch(AppAction.SearchAction.RefreshLoading(false)) } .doOnSuccess { repos -> dispatcher.dispatch(AppAction.DomainAction.PutRepos(repos)) } .map { repos -> AppAction.SearchAction.RefreshRepos(repos) } } } } } 102 DomainStateΛมߋ
  56. class SearchActionCreator @Inject constructor( private val repository: GitHubRepository ) {

    fun fetchSearchRepositories(query: String): AsyncActionType { return object : AsyncActionType { override fun execute(dispatcher: Dispatcher): Single<ActionType> { return repository.searchRepositoriesByQuery(query) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .doOnSubscribe { dispatcher.dispatch(AppAction.SearchAction.RefreshLoading(true)) } .doOnEvent { _, _ -> dispatcher.dispatch(AppAction.SearchAction.RefreshLoading(false)) } .doOnSuccess { repos -> dispatcher.dispatch(AppAction.DomainAction.PutRepos(repos)) } .map { repos -> AppAction.SearchAction.RefreshRepos(repos) } } } } } 103 ݕࡧ݁ՌΛը໘ʹ൓ө
  57. class StarActionCreator @Inject constructor( private val repository: GitHubRepository ) {

    fun starRepo(repo: Repo): AsyncActionType { return object : AsyncActionType { override fun execute(dispatcher: Dispatcher): Single<ActionType> { return repository.starRepo(repo) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .doOnSuccess { repo -> dispatcher.dispatch(AppAction.DomainAction.StarRepo(repo)) } .map { repo -> AppAction.StarAction.AddRepo(repo) } } } } } 105
  58. class StarActionCreator @Inject constructor( private val repository: GitHubRepository ) {

    fun starRepo(repo: Repo): AsyncActionType { return object : AsyncActionType { override fun execute(dispatcher: Dispatcher): Single<ActionType> { return repository.starRepo(repo) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .doOnSuccess { repo -> dispatcher.dispatch(AppAction.DomainAction.StarRepo(repo)) } .map { repo -> AppAction.StarAction.AddRepo(repo) } } } } } 106 ελʔॲཧΛ࣮ߦ
  59. class StarActionCreator @Inject constructor( private val repository: GitHubRepository ) {

    fun starRepo(repo: Repo): AsyncActionType { return object : AsyncActionType { override fun execute(dispatcher: Dispatcher): Single<ActionType> { return repository.starRepo(repo) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .doOnSuccess { repo -> dispatcher.dispatch(AppAction.DomainAction.StarRepo(repo)) } .map { repo -> AppAction.StarAction.AddRepo(repo) } } } } } 107 ελʔॲཧͷ׬ྃޙʹDomainStateΛมߋ
  60. class StarActionCreator @Inject constructor( private val repository: GitHubRepository ) {

    fun starRepo(repo: Repo): AsyncActionType { return object : AsyncActionType { override fun execute(dispatcher: Dispatcher): Single<ActionType> { return repository.starRepo(repo) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .doOnSuccess { repo -> dispatcher.dispatch(AppAction.DomainAction.StarRepo(repo)) } .map { repo -> AppAction.StarAction.AddRepo(repo) } } } } } 108 ελʔҰཡը໘ʹϦϙδτϦΛ௥Ճ
  61. ςετίʔυ • AppState/AppAction • ೖΕ෺ΫϥεͳͷͰςετ͸ෆཁ • AppReducer • ঢ়ଶมߋͷܭࢉ͕ਖ਼͘͠ߦΘΕΔ͔ΛνΣοΫ͢Δ •

    ७ਮͳؔ਺ͱ࣮ͯ͠૷͍ͯ͠ΔͷͰඇৗʹςετָ͕ • AppStore • ঢ়ଶͷอ࣋ɾมߋɾ௨஌͕ਖ਼͘͠ߦΘΕΔ͔ΛνΣοΫ͢Δ • RxJava͕བྷΉ෦෼͸TestObserverΛར༻ͯ͠ςετ͢Δ 109
  62. AppReducer • ঢ়ଶมߋͷܭࢉ͕ਖ਼͘͠ߦΘΕΔ͔ΛνΣοΫ͢Δ var state = AppState() assert(!state.search.isLoading) val action

    = AppAction.SearchAction.RefreshLoading(isLoading = true) state = AppReducer.reduce(state, action) assert(state.search.isLoading) 110
  63. AppStore • ঢ়ଶͷอ࣋ɾมߋɾ௨஌͕ਖ਼͘͠ߦΘΕΔ͔ΛνΣοΫ͢Δ val state = AppState() val store =

    AppStore(state) val observer = TestObserver<AppState>() store.observable().subscribe(observer) observer.assertValueCount(1) observer.assertValueAt(0, state) val action = AppAction.SearchAction.RefreshLoading(true) store.dispatch(action) observer.assertValueCount(2) observer.assertValueAt(1, state.copy(search = state.search.copy(isLoading = true))) 111
  64. ϝϦοτɾσϝϦοτ • ϝϦοτ • ୯ํ޲σʔλϑϩʔʹΑͬͯίʔυ͕௥͍΍͘͢ͳΔ • ঢ়ଶ؅ཧͷ࣮૷͕ΞϓϦશମͰ౷Ұ͞ΕΔ • AndroidͷϥΠϑαΠΫϧʹࠨӈ͞Εͳ͘ͳΔ •

    σϝϦοτ • Reduxͷࢥ૝Λཧղ͢Δ·Ͱ͸։ൃεϐʔυ͕μ΢ϯ • ίʔυྔ͕૿͑ɺσʔλਖ਼نԽ·ͰؚΊΔͱ࣮૷͕େม 113
  65. ଞͷΞʔΩςΫνϟͱͷؔ܎ੑ 114 Presentation Model MVP ̋ ✕ MVVM ̋ ✕

    Flux / Redux ̋ ✕ AAC ̋ ✕ Layered Architecture ✕ ̋
  66. ·ͱΊ • جૅฤ • Stateɿঢ়ଶΛදݱ͢Δ΋ͷ • Actionɿঢ়ଶͷมߋ಺༰Λදݱ͢Δ΋ͷ • Reducerɿ࣍ͷঢ়ଶΛܭࢉ͢Δ΋ͷ •

    Storeɿঢ়ଶอ࣋ͱมߋΛ୲౰͢Δ΋ͷ • Ԡ༻ฤ • ඇಉظॲཧɿMiddlewareͰͷ࣮૷͕ओྲྀ • ը໘ભҠɿReduxͱ͸ผͰ࣮૷͢Δ • σʔλͷਖ਼نԽɿσʔλͷมߋෛՙΛԼ͛Δ • ςετίʔυɿReducer͸؆୯ɺStore͸TestObserverΛ࢖͏ 117