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

Flux de Relax :)

Flux de Relax :)

Flux implementation for Android

Masaki Ogata

August 07, 2016
Tweet

More Decks by Masaki Ogata

Other Decks in Programming

Transcript

  1. Flux de Relax :)
    ANDROID ALLSTARS #2 @dots.
    Masaki Ogata

    View Slide

  2. About me
    Masaki Ogata
    ogaclejapan
    CyberAgent, Inc. / AbemaTV, Inc.
    @ogaclejapan

    View Slide

  3. Flux de Relax :)

    View Slide

  4. What is Flux?

    View Slide

  5. Facebook Flux Architecture
    IUUQTGBDFCPPLHJUIVCJPqVYEPDTPWFSWJFXIUNM
    “Data in a Flux application flows in a single
    direction”

    View Slide

  6. Facebook Flux Architecture
    IUUQTHJUIVCDPNGBDFCPPLqVY

    View Slide

  7. Facebook Flux Architecture
    IUUQTHJUIVCDPNGBDFCPPLqVY
    Observerύλʔϯ

    View Slide

  8. Why Flux?

    View Slide

  9. Why Flux?
    ΞϓϦέʔγϣϯͷ։ൃͰ
    Viewͷঢ়ଶ؅ཧ͕Ұ൪೉͍͠ :(

    View Slide

  10. Why Flux?
    AbemaTVͰඞཁʹͳΔViewͷঢ়ଶ؅ཧ:
    Cast
    ՝ۚ CM
    ࢹௌ༧໿
    ΦϯσϚϯυ
    ը࣭
    ϑΟϥʔ
    etc…

    View Slide

  11. Sample code
    https://github.com/ogaclejapan/
    FluxArchitectureSample
    *OQVU'SBHNFOU
    3FTVMU'SBHNFOU

    View Slide

  12. Flux Architecture Sample

    View Slide

  13. Flux: Dispatcher

    View Slide

  14. Flux: Dispatcher
    N:1
    1:N

    View Slide

  15. Flux: Dispatcher
    /* Store */
    Dispatcher.register(function(payload) {
    switch(payload.actionType) {
    case 'foo':
    ... = payload.data
    // Do something
    }
    }
    /* Action */
    Dispatcher.dispatch({
    actionType: 'foo',
    payload: 'value'
    });
    ຊՈFluxͷ࣮૷Λࢀߟʹͯ͠ΈΔ

    View Slide

  16. Flux: Dispatcher
    JavaͰ࣮૷Λॻ͖௚ͯ͠ΈΔͱ…
    public interface Action { String getType(); }
    public class FooAction implements Action {...}
    /* Action */
    Dispatcher.dispatch(new FooAction(data));
    /* Store */
    Dispatcher.register(new Callback() {
    public void on(Action action) {
    switch (action.getType()) {
    case "foo":
    ... = ((FooAction) action).data;
    break;
    }
    }
    }

    View Slide

  17. Flux: Dispatcher
    JavaͰ࣮૷Λॻ͖௚ͯ͠ΈΔͱ…
    public interface Action { String getType(); }
    public class FooAction implements Action {...}
    /* Action */
    Dispatcher.dispatch(new FooAction(data));
    /* Store */
    Dispatcher.register(new Callback() {
    public void on(Action action) {
    switch (action.getType()) {
    case "foo":
    ... = ((FooAction) action).data;
    break;
    }
    }
    }
    Javaͩͱܕม׵͕ඞཁ :(

    View Slide

  18. Flux: Dispatcher
    IUUQTHJUIVCDPNHSFFOSPCPU&WFOU#VT
    // Define events:
    public class MessageEvent { /* Additional fields if needed */ }
    // Prepare subscribers: Register your subscriber
    eventBus.register(this);
    // Declare your subscribing method:
    @Subscribe
    public void onEvent(AnyEventType event) {/* Do something */};
    // Post events:
    eventBus.post(event);
    …EventBusͰΑ͘Ͷ͐ʁ

    View Slide

  19. Flux: Dispatcher
    public class Dispatcher {
    private final EventBus bus;
    public Dispatcher() {
    bus = EventBus.builder()
    ...
    .build();
    }
    public void dispatch(Object payload) {
    bus.post(payload);
    }
    public void register(Object observer) {
    bus.register(observer);
    }
    public void unregister(Object observer) {
    bus.unregister(observer);
    }
    }

    View Slide

  20. Flux: Action

    View Slide

  21. Flux: Action
    Actionͷσʔλϑϩʔ ٩(•౪• ٩)

    View Slide

  22. Flux: Action
    View͔ΒͷೖྗʹΑΓσʔλ͕ྲྀΕͯ͘Δ

    View Slide

  23. Flux: Action
    σʔλιʔεʹඞཁͱͳΔσʔλΛऔΓʹߦ͘

    View Slide

  24. Flux: Action
    σʔλ͕ू·ͬͨΒDispatcher΁σʔλΛྲྀ͢

    View Slide

  25. Flux: Action
    Web, DB, DevicesΛ֎෦αʔϏεͱͯ͠ଊ͑Δ

    View Slide

  26. Flux: Action
    @Inject GitHubApi gitHubApi;
    private final Dispatcher dispatcher;
    @Inject public UserSearchAction(Dispatcher dispatcher) {
    this.dispatcher = dispatcher;
    }
    public void findFollower(String userId) { ... }
    public void findFollower(String userId, int nextPage) {
    gitHubApi.followers(userId, nextPage)
    .doOnSubscribe(() -> dispatchState(LoadingState.LOADING))
    .subscribe(users -> {
    dispatcher.dispatch(new SearchResultListChangedEvent(
    userId, users, users.nextPage()));
    dispatchState(users.hasMore()
    ? LoadingState.LOADABLE
    : LoadingState.FINISHED);
    }, ...);
    }

    View Slide

  27. Flux: Action
    Tips: ֎෦I/Fͷ໭Γ஋ΛRxͰ౷Ұ͓ͯ͘͠
    @Inject GitHubApi gitHubApi;
    public void findFollower(String userId, int nextPage) {
    Observable.zip(
    gitHubApi.followers(userId, nextPage),
    gitHubApi.user(userId),
    this::doSomething)
    .subscribe(...)
    }

    View Slide

  28. Flux: Action
    Tips: Ωϟϯηϧॲཧ͕ඞཁͳͱ͖
    // e.g. ॲཧΛݺͼग़͠ଆͰΩϟϯηϧ͢Δ
    public Subscription findFollower(String userId, int nextPage) {
    return gitHubApi.followers(userId, nextPage)
    .subscribe(...);
    }
    // e.g. ॲཧ͕࣮ߦதͳΒΩϟϯηϧ͢Δ
    private Subscription subs = Subscriptions.empty();
    public void findFollower(String userId, int nextPage) {
    if (!subs.isUnsubscribed()) subs.unsubscribe();
    subs = gitHubApi.followers(userId, nextPage)
    .subscribe(...);
    }

    View Slide

  29. Flux: Store

    View Slide

  30. Flux: Store
    Storeͷσʔλϑϩʔ ₍₍ (ง ˙ω˙)ว ⁾⁾

    View Slide

  31. Flux: Store
    σʔλΛड͚औΔͨΊʹCallbackΛొ࿥͢Δ

    View Slide

  32. Flux: Store
    Action͔ΒσʔλΛྲྀ͢ͱCallback΁ྲྀΕͯ͘Δ

    View Slide

  33. Flux: Store
    σʔλΛड͚औͬͨΒStore಺σʔλΛߋ৽͢Δ

    View Slide

  34. Flux: Store
    σʔλΛߋ৽ͨ͠ΒView΁มߋΛ௨஌͢Δ

    View Slide

  35. Flux: Store
    @ActivityScope
    public class UserSearchStore {
    @Inject
    public UserSearchStore(Dispatcher dispatcher, ActivityLifecycleHook hook) {
    hook.addOnCreate(() -> dispatcher.register(this));
    hook.addOnDestroy(() -> dispatcher.unregister(this));
    }
    Dispatcher΁CallbackΛొ࿥͢Δ

    View Slide

  36. Flux: Store
    @Singleton
    @ActivityScope
    public class UserSearchStore {
    @Inject
    public UserSearchStore(Dispatcher dispatcher, ActivityLifecycleHook hook) {
    dispatcher.register(this);
    hook.addOnCreate(() -> dispatcher.register(this));
    hook.addOnDestroy(() -> dispatcher.unregister(this));
    }
    Dispatcher΁CallbackΛొ࿥͢Δ

    View Slide

  37. Flux: Store
    CallbackॲཧͰࣗ਎ͷঢ়ଶΛߋ৽͢Δ
    private final ObservableField state = new
    ObservableField<>(LoadingState.LOADABLE);
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void on(SearchLoadingStateChangedEvent event) {
    state.set(event.state);
    }

    View Slide

  38. Flux: Store
    ঢ়ଶมߋΛ௨஌͢ΔͨΊͷϝιουΛެ։͢Δ
    private final ObservableField state = new
    ObservableField<>(LoadingState.LOADABLE);
    public Disposer addOnLoadingStateChanged(
    OnFieldChangedCallback cb) {
    state.addOnPropertyChangedCallback(cb);
    return Disposers.from(() -> removeOnLoadingStateChanged(cb));
    }
    public void removeOnLoadingStateChanged(
    OnFieldChangedCallback cb) {
    state.removeOnPropertyChangedCallback(cb);
    }

    View Slide

  39. Flux: Store
    Tips: ObservableXXͷ୅ΘΓʹRxΛ࢖͏
    private final BehaviorSubject state =
    BehaviorSubject.create(LoadingState.LOADABLE);
    public Observable state() {
    return state.asObservable();
    }
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void on(SearchLoadingStateChangedEvent event) {
    state.onNext(event.state);
    }

    View Slide

  40. Flux: View

    View Slide

  41. Flux: View
    Viewͷσʔλϑϩʔ ٩(๑´3ʆ๑)۶

    View Slide

  42. Flux: View
    σʔλΛड͚औΔͨΊStore΁CallbackΛొ࿥͢Δ

    View Slide

  43. Flux: View
    σʔλ͕ߋ৽͞ΕͨΒCallback͕ݺ͹ΕΔ

    View Slide

  44. Flux: View
    σʔλΛड͚औͬͨΒը໘Λߋ৽͢Δ

    View Slide

  45. Flux: View
    ৽ͨͳೖྗ͕ൃੜͨ͠ΒAction΁σʔλΛྲྀ͢

    View Slide

  46. Flux: View
    Storeͷঢ়ଶʹԠͯ͡ViewΛߋ৽͢Δ
    @Inject UserSearchStore userSearchStore;
    private final OnListChangedCallback resultListChanged =
    new OnListChangedCallback() {
    @Override
    public void onChanged(ObservableList sender) {
    binding.setItemCount(sender.size());
    }
    };
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    ...
    userSearchStore.addOnListChanged(resultListChanged).addTo(this);
    }

    View Slide

  47. Flux: View
    Storeͷঢ়ଶʹԠͯ͡AdapterΛߋ৽͢Δ
    @Inject
    public UserSearchListAdapter(UserSearchStore store,
    ActivityLifecycleHook hook) {
    this.store = store;
    OnListChangedCallback cb = OnListChangedCallback.delegateTo(this);
    hook.addOnCreate(() -> store.addOnListChanged(cb));
    hook.addOnDestroy(() -> store.removeOnListChanged(cb));
    }
    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
    User user = store.getItemAt(position);
    ...
    }
    @Override
    public int getItemCount() {
    return store.getItemCount();
    }

    View Slide

  48. Flux: View
    ActionʹॲཧΛҕৡ͢Δ
    @Inject UserSearchAction userSearchAction;
    // SearchInputFragment
    @Override public void onViewCreated(View view, …) {
    binding.searchButton.setOnClickListener(v -> {
    hideKeyboard(binding.searchInputText.getWindowToken());
    Optional.ofNullable(binding.searchInputText.getText())
    .map(Editable::toString)
    .filter(it -> !it.isEmpty())
    .ifPresent(userSearchAction::findFollower);
    });
    // SearchResultFragment
    @Override public void onLoadMore() {
    userSearchAction.findFollower(
    userSearchStore.getUserId(), userSearchStore.getNextPage());
    }

    View Slide

  49. Flux: View
    Tips: ObservableXXͷ୅ΘΓʹRxΛ࢖͏
    import com.trello.rxlifecycle.components.support.RxFragment;
    public class SearchResultFragment extends RxFragment {
    @Inject UserSearchStore userSearchStore;
    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
    ...
    userSearchStore.state()
    .map(it -> it == LoadingState.LOADING)
    .compose(bindToLifecycle())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(binding::setIsLoading);
    }

    View Slide

  50. Conclusion
    Pros :)
    • Viewؒͷґଘ͕ܹݮͯ͠ɺѹ౗తײँʂ
    • ໾ׂ͕໌֬ͳͷͰ։ൃऀͷ࣮૷͕౷Ұ͞Ε΍͍͢
    • ୯ํ޲ͳͷͰίʔυ͕௥͍΍͍͢

    View Slide

  51. Conclusion
    Cons :(
    • γϯϓϧͳػೳͩͱएׯ৑௕ʹײ͡Δͱ͖΋...
    • ղ์ϛεΔͱଈϝϞϦϦʔΫʘ(^o^)ʗ
    • جຊτϥΠˍΤϥʔ (ʀ´∀ʆ)

    View Slide

  52. Let's Flux de Relax :)

    View Slide