Slide 1

Slide 1 text

TESTABLE REDUX ARCHITECTURE ON ANDROID Elvis Lin @AndroidTaipei
 2017-06-17

Slide 2

Slide 2 text

AGENDA • Why Not MVC • 3 Principle of Redux • Components of Redux • Sample Code • How to Test

Slide 3

Slide 3 text

WHY NOT MVC? • 太多狀狀態 • 狀狀態維護困難 • Model 跟 View 互動複雜

Slide 4

Slide 4 text

Event Controller /Activity Model Model Model Model Model View View View View View

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

WHY NOT MUTABLE STATE? • 需要⼿手動的去通知狀狀態的變化 • 資料/狀狀態會在我們不預期的時候發⽣生改變 • 非執⾏行行緒安全(Not thread safe) • 較難測試

Slide 7

Slide 7 text

REDUX PRINCIPLES

Slide 8

Slide 8 text

THREE PRINCIPLES (REDUX) • Single source of truth
 The state of your whole application is stored in an object tree within a single store. • State is read-only
 The only way to change the state is to emit an action. • Changes are made with pure functions
 Pure reducers specify how the state tree is transformed.

Slide 9

Slide 9 text

SINGLE SOURCE OF TRUTH • Traditional Style • 多個 Manager / DateStore 保存、維護所有資 料 • Redux Style • ⼀一個 Store/State 保存所有資料

Slide 10

Slide 10 text

STATE IS READ-ONLY • Traditional Style • Data/State是可變的(mutable) • Redux Style • State 是不可變的(immutable),⽤用新的 State 取代舊的 state

Slide 11

Slide 11 text

CHANGES ARE MADE WITH PURE FUNCTIONS • Functional: • Only generate output from input, doesn’t modify other states. • Y = f(x), • ex: squareRoot(4) = 2

Slide 12

Slide 12 text

CHANGES ARE MADE WITH PURE FUNCTIONS • Traditional Style • 改變 state 的 method 是有 side-effect 的 • Redux Style • Reducer 接受 current state,輸出 new state

Slide 13

Slide 13 text

UNIDIRECTIONAL ARCHITECTURE

Slide 14

Slide 14 text

UNIDIRECTIONAL Source: http://www.michaelridland.com/xamarin/mvvm-mvc-is-dead-is-unidirectional-a-mvvm-mvc-killer/

Slide 15

Slide 15 text

HOW STORE/STATE UPDATE

Slide 16

Slide 16 text

REDUX

Slide 17

Slide 17 text

COMPONENTS OF REDUX • Store: dispatch action, and notify the subscriber • State: key the data of the app • Action: Define which should be modify, and provide necessary parameter • Reducer: Modify the state by the action • View/Controller/Presenter: Get the event and dispatch it to store

Slide 18

Slide 18 text

FLOW OF REDUX • Store object holds and maintains the state • To change the state application can dispatch actions to Store • For each action Store compute new state using pure function f(s, a) (called reducer) • Store replace current state with new state

Slide 19

Slide 19 text

REDUX ARCH. Source: https://github.com/ReSwift/ReSwift

Slide 20

Slide 20 text

REDUCTOR: 
 REDUX LIBRARY

Slide 21

Slide 21 text

REDUCTOR • Use Annotation to create boilerplate code • All the boilerplate is written by a machine

Slide 22

Slide 22 text

STATE @CombinedState
 @AutoValue
 public abstract class AppState {
 public abstract List notes();
 
 public abstract NotesFilter filter();
 
 public static TypeAdapter typeAdapter(Gson gson) {
 return new AutoValue_AppState.GsonTypeAdapter(gson);
 }
 
 public List getFilteredNotes() {
 List notes = this.notes();
 NotesFilter filter = this.filter();
 return Utils.filter(ConsPStack.from(notes), note ->
 filter == NotesFilter.ALL
 || filter == NotesFilter.CHECKED && note.checked
 || filter == NotesFilter.UNCHECKED && !note.checked);
 }
 }


Slide 23

Slide 23 text

ACTION @ActionCreator
 public interface NotesActions {
 String ADD_ACTION = "ADD_ITEM";
 String TOGGLE = "TOGGLE";
 String REMOVE_ITEM = "REMOVE_ITEM";
 
 @ActionCreator.Action(NotesActions.ADD_ACTION)
 Action add(int id, String content);
 
 @ActionCreator.Action(NotesActions.REMOVE_ITEM)
 Action remove(int id);
 
 @ActionCreator.Action(NotesActions.TOGGLE)
 Action toggle(int id);
 }


Slide 24

Slide 24 text

REDUCER @AutoReducer
 public abstract class NotesListReducer implements Reducer> {
 
 @AutoReducer.InitialState
 List initialState() {
 return TreePVector.empty();
 }
 
 @Action(value = NotesActions.ADD_ACTION,
 from = NotesActions.class)
 public List add(List state, int id, String content) {
 return TreePVector.from(state).plus(new Note(id, content, false));
 }
 
 
 
 public static NotesListReducer create() {
 return new NotesListReducerImpl();
 }
 }

Slide 25

Slide 25 text

Sample Code

Slide 26

Slide 26 text

– Reductor Example https://github.com/Yarikx/reductor

Slide 27

Slide 27 text

TESTING

Slide 28

Slide 28 text

重複的程式碼已經⾃自動產⽣生 • 只剩下 Data & Function • State: Data • Reducer: Function

Slide 29

Slide 29 text

STATE • Inherit to initialize data • Test the subclass of

Slide 30

Slide 30 text

REDUCER • Each method of a reducer is a pure functional method • So, It’s a piece of cake!

Slide 31

Slide 31 text

延伸問題 • 如何處理理非同步? • 使⽤用 midleware • ⾃自⼰己修改 reductor!! • 頻繁的 new state 造成 GC?

Slide 32

Slide 32 text

No content

Slide 33

Slide 33 text

REFERENCE • Redux for Android. Predictable state container library for Java/Android
 https://github.com/Yarikx/reductor • Unidirectional data flow on Android using Kotlin
 https://speakerdeck.com/cesarvaliente/unidirectional-data- flow-on-android-using-kotlin

Slide 34

Slide 34 text

CONTACT ME • @AndroidTaipei • Blog: https://blog.elvismetaphor.me • Slides: https://speakerdeck.com/elvismetaphor • GitHub: https://github.com/elvismetaphor