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

Android Architecture

Android Architecture

Android Architecture presentation @dutchaug meetup at ING

Jordy Langen

June 09, 2017
Tweet

Other Decks in Programming

Transcript

  1. Architecture Goals • Highly testable • Highly maintainable and easy

    reviewing • Separations of concern • Avoid the God of all things (Activity) as much as possible • Activity != View • Minimal amount of boiler plate code • Keep it simple, stupid • Dependency injection friendly • Everything is a stream (Rx) • Reusable components • lol not fragments
  2. Dashboard Presenter Dashboard Model Dashboard View data class Profile(val id:

    Int, var name: String = "", @Gender var gender: Int = FEMALE, var birthDate: Date = Date(), var height: Int = 0, var weightGoal: Int = 0, var goals: List<Int> = listOf(), val measurements: MutableList<Measurement> = mutableListOf() // other properties )
  3. Dashboard Presenter Dashboard Model Dashboard View class DashboardView @JvmOverloads constructor(context:

    Context, attrs: AttributeSet? = null, defStyle: Int = 0) : LinearLayout(context, attrs, defStyle) { fun showProfile(profile: Profile) { ... } fun showMeasurementsCallToAction() { ... } fun showLatestMeasurement() { ... } }
  4. Dashboard Presenter Dashboard Model Dashboard View class DashboardViewCoordinator( private val

    profilesRepository: ProfilesRepository) : Coordinator() { private lateinit var dashboardView: DashboardView override fun attach(view: View) { dashboardView = view as DashboardView profilesRepository.getActiveProfile() .subscribe{ profile -> dashboardView.showProfile(profile) if (profile.measurements.isEmpty()) { dashboardView.showMeasurementsCallToAction() } else { dashboardView.showLatestMeasurement() } ... } } } Simple MVWhatever for Android: https://github.com/square/coordinators
  5. What about • Presenter state • Requesting permissions • startActivityForResult

    • Component to component communication • (presenter to presenter)
  6. Presenter View EventBus (Rx) Activity request permissions or results observes

    observes notifies Requesting permissions / startActivityForResult
  7. Dashboard Presenter Dashboard View EventBus (Rx) Weight Presenter Weight View

    sends event “update weight” observes Component to component communication
  8. Dashboard Presenter Dashboard View EventBus (Rx) Weight Presenter Weight View

    sends event “update weight” observes X Presenter X View observes Component to component communication
  9. Architecture Goals • Highly testable • Highly maintainable and easy

    reviewing • Separations of concern • Avoid the God of all things (Activity) as much as possible • Activity != View • Minimal amount of boiler plate code • Keep it simple, stupid • Dependency injection friendly • Everything is a stream (Rx) • Reusable components • lol not fragments
  10. • Activity != View • Minimal amount of boiler plate

    code • Keep it simple, stupid • Dependency injection friendly • Everything is a stream (Rx) • Reusable components • lol not fragments • Minimal amount of logic in presenters • Separation of data storage and business logic • Proper models • Better way of handling user interaction
  11. data class VaultsViewState(val isLoading: Boolean, val vaults: List<Vault>) Intent Model

    View data class VaultsViewState( val isLoading: Boolean, val vaults: List<Vault>) Entity Model data class Vault( val name: String, val path: String)
  12. data class VaultsViewState(val isLoading: Boolean, val vaults: List<Vault>) Intent Model

    View data class VaultsViewState( val isLoading: Boolean, val vaults: List<Vault>) Entity Model data class Vault( val name: String, val path: String) I realized we never really had a “model”
  13. data class VaultsViewState(val isLoading: Boolean, val vaults: List<Vault>) Intent Model

    View class VaultsView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : RelativeLayout(context, attrs, defStyleAttr), StateRenderer<VaultsViewState> override fun render(state: VaultsViewState) { if (state.isLoading) { vaults_view_progressbar.visibility = View.VISIBLE updateAdapter(emptyList()) } else { vaults_view_progressbar.visibility = View.GONE updateAdapter(state.vaults) } } }
  14. data class VaultsViewState(val isLoading: Boolean, val vaults: List<Vault>) Intent Model

    View class VaultsView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : RelativeLayout(context, attrs, defStyleAttr), StateRenderer<VaultsViewState> override fun render(state: VaultsViewState) { if (state.isLoading) { vaults_view_progressbar.visibility = View.VISIBLE updateAdapter(emptyList()) } else { vaults_view_progressbar.visibility = View.GONE updateAdapter(state.vaults) } } }
  15. data class VaultsViewState(val isLoading: Boolean, val vaults: List<Vault>) Intent Model

    View class VaultsView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : RelativeLayout(context, attrs, defStyleAttr), StateRenderer<VaultsViewState> val openVaultIntents: Observable<Vault> get() = onVaultClickRelay // a RxRelay PublishRelay override fun render(state: VaultsViewState) { // render logic } }
  16. Presenter class VaultsViewCoordinator(var vaultService: VaultService) : Coordinator<VaultsViewState, VaultsView>() { override

    var state: VaultsViewState = VaultsViewState(true, emptyList()) override fun attach(view: VaultsView) { view.render(state) // tying it all together here } // ... }
  17. abstract class Coordinator<TState, in TStateRenderer : StateRenderer<TState>> { var isAttached:

    Boolean = false internal set abstract var state: TState open fun attach(view: TStateRenderer) { } open fun detach(view: TStateRenderer) { } } Presenter state
  18. class StateStore { // works based on the assumption that

    view id + activity name is unique // if you use an id twice in the same view tree, well that's just on you companion object { private val states: MutableList<StateHolder<*>> = mutableListOf() fun <TState> update(id: Int, state: TState, context: Context) { ... } fun <TState> resolve(id: Int, context: Context): TState? { ... } fun markFinished(context: Context) { ... } } private data class StateHolder<out TState>( val viewId: Int, val state: TState, val contextName: String, var activityFinished: Boolean = false) } Presenter state
  19. internal class Binding<in TState, TView : StateRenderer<TState>>( private val coordinator:

    Coordinator<TState, TView>, private val view: View) : View.OnAttachStateChangeListener { private var attached: View? = null override fun onViewAttachedToWindow(attachedView: View) { attached = attachedView coordinator.isAttached = true val state = StateStore.resolve<TState>(view.id, view.context) if (state != null) { coordinator.state = state } StateStore.update(view.id, coordinator.state, view.context) coordinator.attach(view as TView) } override fun onViewDetachedFromWindow(view: View) { StateStore.update(view.id, coordinator.state, view.context) attached = null coordinator.detach(this.view as TView) coordinator.isAttached = false this.view.setTag(R.id.coordinator, null) } } https://github.com/square/coordinators/…/Binding.java Presenter state
  20. class VaultsViewCoordinator(var vaultService: VaultService) : Coordinator<VaultsViewState, VaultsView>() { override var

    state: VaultsViewState = VaultsViewState(true, emptyList()) override fun attach(view: VaultsView) { view.render(state) } // ... } Presenter state
  21. class VaultsViewCoordinator(var vaultService: VaultService) : Coordinator<VaultsViewState, VaultsView>() { override var

    state: VaultsViewState = VaultsViewState(true, emptyList()) override fun attach(view: VaultsView) { view.render(state) } // ... } Presenter state
  22. VaultsView Coordinator Vault Service Vault Repository Presentation logic Business logic

    Data access (also applies to MVP) Minimal amount of logic in presenters
  23. (also applies to MVP) VaultsView Coordinator Vault Service Vault Repository

    Presentation logic Business logic Data access VaultView Coordinator Component to component communication selectedVault: Observable<Vault>
  24. VaultsView Coordinator Vault Service Vault Repository Presentation logic Business logic

    Data access (also applies to MVP) Permission Service Activity observe permission requests request(permission: String) : Observable<String> Requesting permissions
  25. (also applies to MVP) fun findAll(): Observable<List<Vault>> { return permissionService.request(Manifest.permission.READ_EXTERNAL_STORAGE)

    .flatMap { grantedPermission -> Observable.just(grantedPermission == Manifest.permission.READ_EXTERNAL_STORAGE)) } .flatMap { isGranted -> if (isGranted) vaultRepository.findAll() else Observable.empty() } } Requesting permissions
  26. Conclusion Highly testable code Reusable components Separation of data storage

    and business logic No crazy amount of magic needed to implement
  27. Special thanks to Pieter Otten for reviewing and saying “Is

    that really the style you are going for? This is not the final version is it?” “Really?!”
  28. L Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean

    commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc, quis gravida magna mi a libero. Fusce vulputate eleifend sapien. Vestibulum purus quam, scelerisque ut, mollis sed, nonummy id, metus. Nullam accumsan lorem in dui. Cras ultricies mi eu turpis hendrerit fringilla. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; In ac dui quis mi consectetuer lacinia. L Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc, quis gravida magna mi a libero. Special thanks to Pieter Otten