Hey! My name is Vitaly Sulimov, I am an Android developer at Wheely, and today I would like to talk to you about the architecture of mobile applications. Namely, how we at the company applied the Redux architecture to our two applications and what came of it.
Disclaimer # 1
Android- 2016- , MVC, MVP, Moxy Arello Mobile, Clean Architecture Redux. — . , , . , , , . — Android-.
?
Redux, , :
1. View
View — , -, , , — (, , , , Pull To Refresh, ..)
2. , - /
, .
— , runtime, , , .
3.
, , . , / ..
4.
, , , - . , , : , .
Redux, !
Redux Android-, , , Android.
Redux . , Store, , , - , , - . , , Redux.
State
, Redux. () .
Store
Store (State) , Middleware Reducer.
API , (Action), .
Action
, , Store. ( ). .
Reducer
, (Action). , , () ( , , , ..).
, Reducer Copy-on-write , .
Middleware
Middleware , (Action) , , Reducer.
Middleware , - .
Redux Android?
. Android (Action), , , Activity, View, BroadcastReceiver - Action , ( ).
Talk is cheap. Show me the code.
, , Redux , . Counter, , . . .
?
Android Studio Redux Kotlin.
Redux, Android. , , , !
#2
, Rx, Coroutines, - , , . KISS, , .
, Redux? , Copy-on-write. , Kotlin - data class.
ApplicationState.kt
data class ApplicationState(
val counter: Int = 0
)
, , .
Action Redux. Action . Middleware Reducer, sealed class’, , . .
CounterAction.kt
sealed class CounterAction : Action {
object Increment : CounterAction()
object Reset : CounterAction()
}
Reducer
, Reducer<S>, S - , .. ApplicationState. - - reduce. , .
CounterReducer.kt
object CounterReducer : Reducer<ApplicationState> {
override fun reduce(action: Action, state: ApplicationState): ApplicationState =
when (action) {
is CounterAction.Increment ->
state.copy(counter = state.counter.inc())
is CounterAction.Reset ->
state.copy(counter = 0)
else ->
state
}
}
Store
, Store.
Store . - AbstractStore<S> . Middleware Reducer.
ApplicationStore.kt
class ApplicationStore(
initialState: ApplicationState,
middlewares: List<Middleware<ApplicationState>>,
reducers: List<Reducer<ApplicationState>>
) : AbstractStore<ApplicationState>(initialState, middlewares, reducers)
ApplicationStore, Middleware Reducer. Store, ApplicationState - AppComponent Store .
AppComponent.kt
object AppComponent {
val store = ApplicationStore(
initialState = ApplicationState(),
middlewares = emptyList(),
reducers = listOf(CounterReducer)
)
}
, , .
ReduxFunctions.kt
fun dispatch(action: Action) =
AppComponent.store.dispatch(action)
fun subscribe(subscription: Subscription<ApplicationState>) =
AppComponent.store.subscribe(subscription)
fun unsubscribe(subscription: Subscription<ApplicationState>) =
AppComponent.store.unsubscribe(subscription)
, , , , reducer, , . , . , reducer’a, , store . , UI!
Android-. (Single Activity / Multiple Activities / Fragments?), , - Activity View. Activity View, .
CounterView.kt
class CounterView(
context: Context
) : FrameLayout(context) {
private val counterSubscription = SubStateSubscription<ApplicationState, Int>(
transform = { it.counter },
onStateChange = { state: Int, _: Boolean -> handleCounterStateChange(state) }
)
private lateinit var counterTextView: TextView
private lateinit var floatingActionButton: FloatingActionButton
init {
inflate(context, R.layout.view_counter, this)
findViewsById()
setOnClickListeners()
}
private fun findViewsById() {
counterTextView = findViewById(R.id.counterTextView)
floatingActionButton = findViewById(R.id.floatingActionButton)
}
private fun setOnClickListeners() {
floatingActionButton.setOnClickListener { dispatch(CounterAction.Increment) }
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
subscribeToStateChanges()
}
private fun subscribeToStateChanges() {
subscribe(counterSubscription)
}
override fun onDetachedFromWindow() {
unsubscribeFromStateChanges()
super.onDetachedFromWindow()
}
private fun unsubscribeFromStateChanges() {
unsubscribe(counterSubscription)
}
private fun handleCounterStateChange(state: Int) {
counterTextView.text = state.toString()
}
}
, . SubStateSubscription, , , - , , Rx, map(), - .
, lateinit var View.
. XML-, Floating Action Button. dispatch CounterAction.Increment, .
OnViewAttached / Detached from window.
, , . , TextView.
CounterView.kt
... counterTextView.text = state.toString() ...
! .
, , View , counter ApplicationState, , by design, Application View , … , , , ( “”). ? .
, Redux Android
, Android Redux. . AppCompatActivity, AppCompatActivity : Activity ActivityLifecycleAction ( ) . - AppCompatActivity Store, . .
MainActivity.kt
class MainActivity : AppCompatActivity<ApplicationState>() {
private lateinit var contentViewGroup: ViewGroup
override fun getStore(): Store<ApplicationState> =
AppComponent.store
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
findViewsById()
addCounterView()
}
private fun findViewsById() {
contentViewGroup = findViewById(R.id.contentViewGroup)
}
private fun addCounterView() {
contentViewGroup.addView(CounterView(context = this))
}
}
Middleware
- MIddleware. , : Activity (onDestroy) isFInishing == true - .
isFinishing , true, , , false - , .
, , . , Middleware<S>, S - handleAction().
ActivityLifecycleMiddleware.kt
object ActivityLifecycleMiddleware : Middleware<ApplicationState> {
override fun handleAction(
action: Action,
state: ApplicationState,
next: Next<ApplicationState>
): Action {
val newAction = when (action) {
is ActivityLifecycleAction.OnDestroy ->
handleActivityOnDestroy(action)
else ->
action
}
return next(newAction, state)
}
private fun handleActivityOnDestroy(action: ActivityLifecycleAction.OnDestroy): Action =
if (action.isFinishing) CounterAction.Reset else action
}
, . ActivityLifecycleAction.OnDestroy Reducer, Middleware, , . , isFinishing == true, Reducer CounterAction.Reset, , false - , , , . middleware AppComponent-.
AppComponent.kt
store = ApplicationStore( initialState = ApplicationState(), middlewares = listOf(ActivityLifecycleMiddleware), reducers = listOf(CounterReducer) )
!
Redux. , , — . , JavaScript. , , , . . - - Action. - Middleware, - Reducer. View , .
, , Counter, Middleware Reducer , . Redux- — “”, Android ( ), API OpenWeatherMap. .
https://gitlab.com/v.sulimov/android-redux-kotlin
https://gitlab.com/v.sulimov/android-redux-demo
https://gitlab.com/v.sulimov/android-openweather-kotlin
, Redux Android, . , , , , Redux , , . , - .
, , .
, . Wheely.