Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Flux de Relax :)
Search
Masaki Ogata
August 07, 2016
Programming
62k
13
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Flux de Relax :)
Flux implementation for Android
Masaki Ogata
August 07, 2016
More Decks by Masaki Ogata
See All by Masaki Ogata
Jetpack Composeで始めるServer Cache State
ogaclejapan
2
330
AbemaTVを支えるアプリの優しさ / abematv_devcon_2017
ogaclejapan
3
6.9k
Architecture Components - Lifecycle library
ogaclejapan
3
2.9k
How to keep data between orientation changes
ogaclejapan
7
2.7k
Your app name
ogaclejapan
1
3k
Dagger2 has been released!
ogaclejapan
3
3.4k
Other Decks in Programming
See All in Programming
パズルゲームの作り方 / how to make puzzle games
kaityo256
PRO
2
250
スマートフォンでモールス信号を送受信する 〜スマートフォンのLEDとカメラで作る光通信の設計と実装〜
atsuki_seo
0
210
IBM Bob Dojo #1 仕様駆動開発入門
oniak3ibm
PRO
0
310
更なる可用性を求めて、5年間運用したKotlinのアプリケーションをGoでリプレイスする話
ken_tunc
0
400
Java 27新機能 / Java 27 new features
kishida
2
180
[2026-09-26]空論ジェネリックプロセス~テスト資産とAIで紡ぐ、再現可能なパフォーマンスチューニングの話~
tosite
0
230
技術的負債の返済は、AI時代の複利で効く投資 — 経営としての意思決定とその遂行
curekoshimizu
1
2k
アクセシビリティから考える情報設計
high_g_engineer
0
420
Go × SIMDで高速化するベクトル検索 ~ルーフラインモデルでSIMDが効く境界を探れ! ~
po3rin
1
4.8k
C#の現在地 進化の歴史と、AI時代の.NET Everywhere
neuecc
5
4.4k
Webの地図
yosuke_furukawa
PRO
6
4.9k
Verilogで学ぶCPU自作入門.pdf
uyuki234
7
3.8k
Featured
See All Featured
HTML-Aware ERB: The Path to Reactive Rendering @ RubyCon 2026, Rimini, Italy
marcoroth
5
750
Art, The Web, and Tiny UX
lynnandtonic
304
22k
What’s in a name? Adding method to the madness
productmarketing
PRO
24
4.2k
Building Flexible Design Systems
yeseniaperezcruz
330
41k
Java REST API Framework Comparison - PWX 2021
mraible
34
9.7k
Balancing Empowerment & Direction
lara
6
1.3k
Jamie Indigo - Trashchat’s Guide to Black Boxes: Technical SEO Tactics for LLMs
techseoconnect
PRO
0
680
Reality Check: Gamification 10 Years Later
codingconduct
0
2.3k
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
50
10k
Impact Scores and Hybrid Strategies: The future of link building
tamaranovitovic
0
440
How to make the Groovebox
asonas
2
2.5k
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
52
6.1k
Transcript
Flux de Relax :) ANDROID ALLSTARS #2 @dots. Masaki Ogata
About me Masaki Ogata ogaclejapan CyberAgent, Inc. / AbemaTV, Inc.
@ogaclejapan
Flux de Relax :)
What is Flux?
Facebook Flux Architecture IUUQTGBDFCPPLHJUIVCJPqVYEPDTPWFSWJFXIUNM “Data in a Flux application flows
in a single direction”
Facebook Flux Architecture IUUQTHJUIVCDPNGBDFCPPLqVY
Facebook Flux Architecture IUUQTHJUIVCDPNGBDFCPPLqVY Observerύλʔϯ
Why Flux?
Why Flux? ΞϓϦέʔγϣϯͷ։ൃͰ Viewͷঢ়ଶཧ͕Ұ൪͍͠ :(
Why Flux? AbemaTVͰඞཁʹͳΔViewͷঢ়ଶཧ: Cast ՝ۚ CM ࢹௌ༧ ΦϯσϚϯυ ը࣭ ϑΟϥʔ
etc…
Sample code https://github.com/ogaclejapan/ FluxArchitectureSample *OQVU'SBHNFOU 3FTVMU'SBHNFOU
Flux Architecture Sample
Flux: Dispatcher
Flux: Dispatcher N:1 1:N
Flux: Dispatcher /* Store */ Dispatcher.register(function(payload) { switch(payload.actionType) { case
'foo': ... = payload.data // Do something } } /* Action */ Dispatcher.dispatch({ actionType: 'foo', payload: 'value' }); ຊՈFluxͷ࣮Λࢀߟʹͯ͠ΈΔ
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; } } }
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ͩͱܕม͕ඞཁ :(
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ͰΑ͘Ͷ͐ʁ
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); } }
Flux: Action
Flux: Action Actionͷσʔλϑϩʔ ٩(•౪• ٩)
Flux: Action View͔ΒͷೖྗʹΑΓσʔλ͕ྲྀΕͯ͘Δ
Flux: Action σʔλιʔεʹඞཁͱͳΔσʔλΛऔΓʹߦ͘
Flux: Action σʔλ͕ू·ͬͨΒDispatcherσʔλΛྲྀ͢
Flux: Action Web, DB, DevicesΛ֎෦αʔϏεͱͯ͠ଊ͑Δ
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); }, ...); }
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(...) }
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(...); }
Flux: Store
Flux: Store Storeͷσʔλϑϩʔ ₍₍ (ง ˙ω˙)ว ⁾⁾
Flux: Store σʔλΛड͚औΔͨΊʹCallbackΛొ͢Δ
Flux: Store Action͔ΒσʔλΛྲྀ͢ͱCallbackྲྀΕͯ͘Δ
Flux: Store σʔλΛड͚औͬͨΒStoreσʔλΛߋ৽͢Δ
Flux: Store σʔλΛߋ৽ͨ͠ΒViewมߋΛ௨͢Δ
Flux: Store @ActivityScope public class UserSearchStore { @Inject public UserSearchStore(Dispatcher
dispatcher, ActivityLifecycleHook hook) { hook.addOnCreate(() -> dispatcher.register(this)); hook.addOnDestroy(() -> dispatcher.unregister(this)); } DispatcherCallbackΛొ͢Δ
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)); } DispatcherCallbackΛొ͢Δ
Flux: Store CallbackॲཧͰࣗͷঢ়ଶΛߋ৽͢Δ private final ObservableField<LoadingState> state = new ObservableField<>(LoadingState.LOADABLE);
@Subscribe(threadMode = ThreadMode.MAIN) public void on(SearchLoadingStateChangedEvent event) { state.set(event.state); }
Flux: Store ঢ়ଶมߋΛ௨͢ΔͨΊͷϝιουΛެ։͢Δ private final ObservableField<LoadingState> state = new ObservableField<>(LoadingState.LOADABLE);
public Disposer addOnLoadingStateChanged( OnFieldChangedCallback<LoadingState> cb) { state.addOnPropertyChangedCallback(cb); return Disposers.from(() -> removeOnLoadingStateChanged(cb)); } public void removeOnLoadingStateChanged( OnFieldChangedCallback<LoadingState> cb) { state.removeOnPropertyChangedCallback(cb); }
Flux: Store Tips: ObservableXXͷΘΓʹRxΛ͏ private final BehaviorSubject<LoadingState> state = BehaviorSubject.create(LoadingState.LOADABLE);
public Observable<LoadingState> state() { return state.asObservable(); } @Subscribe(threadMode = ThreadMode.MAIN) public void on(SearchLoadingStateChangedEvent event) { state.onNext(event.state); }
Flux: View
Flux: View Viewͷσʔλϑϩʔ ٩(๑´3ʆ๑)۶
Flux: View σʔλΛड͚औΔͨΊStoreCallbackΛొ͢Δ
Flux: View σʔλ͕ߋ৽͞ΕͨΒCallback͕ݺΕΔ
Flux: View σʔλΛड͚औͬͨΒը໘Λߋ৽͢Δ
Flux: View ৽ͨͳೖྗ͕ൃੜͨ͠ΒActionσʔλΛྲྀ͢
Flux: View Storeͷঢ়ଶʹԠͯ͡ViewΛߋ৽͢Δ @Inject UserSearchStore userSearchStore; private final OnListChangedCallback<User> resultListChanged
= new OnListChangedCallback<User>() { @Override public void onChanged(ObservableList<User> sender) { binding.setItemCount(sender.size()); } }; public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { ... userSearchStore.addOnListChanged(resultListChanged).addTo(this); }
Flux: View Storeͷঢ়ଶʹԠͯ͡AdapterΛߋ৽͢Δ @Inject public UserSearchListAdapter(UserSearchStore store, ActivityLifecycleHook hook) {
this.store = store; OnListChangedCallback<User> 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(); }
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()); }
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); }
Conclusion Pros :) • Viewؒͷґଘ͕ܹݮͯ͠ɺѹతײँʂ • ׂ͕໌֬ͳͷͰ։ൃऀͷ࣮͕౷Ұ͞Ε͍͢ • ୯ํͳͷͰίʔυ͕͍͍͢
Conclusion Cons :( • γϯϓϧͳػೳͩͱएׯʹײ͡Δͱ͖... • ղ์ϛεΔͱଈϝϞϦϦʔΫʘ(^o^)ʗ • جຊτϥΠˍΤϥʔ (ʀ´∀ʆ)
Let's Flux de Relax :)