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

Engineering Smart && Building Dumb: Android Design Patterns for Rapid Product Development

Engineering Smart && Building Dumb: Android Design Patterns for Rapid Product Development

Slides from my talk at the NY Android Developers Meetup at OkCupid on 1/23/18 talking about building fully native remotely configurable thin clients on Android. Deserializing layouts and behavior from JSON.
The slides aren't meant to for standalone consumption.

Brandon John-Freso

January 24, 2018
Tweet

Other Decks in Programming

Transcript

  1. D u m b C l i e n t

    M a n t r a : D o n ’ t d o o n t h e c l i e n t w h a t t h e s e r v e r c a n d o . “
  2. 1 D I S P L AY D A T

    A 2 R E C E I V E I N P U T
  3. www.yourdomain.com Fox Slides 7 data class User() data class MatchInfo()

    data class Question() data class Answer() data class Photo() data class Interest() data class Message() …
  4. open class LayoutData( SerializedName("action") val action: OkAction? SerializedName("id") val id:

    String? ) data class PictureTitleSubtitle( SerializedName("picture_url") val pictureUrl: String? SerializedName("picture_bottom_border_color") val pictureBottomBo SerializedName("title") val title: OkText? SerializedName("subtitle") val subtitle: OkText? ) : LayoutData() data class TileAvatarText( SerializedName("avatar_url") val avatarUrl: String? SerializedName("top_text") val topText: OkText? SerializedName("bottom_text") val bottomText: OkText? SerializedName("background_color") val backgroundColor: OkRGBA? SerializedName("top_tile_url") val topTileUrl: String? ) : LayoutData() data class AvatarThreeText( SerializedName("title") val title: OkText? SerializedName("top_text") val topText: OkText? SerializedName("middle_text") val middleText: OkText? SerializedName("bottom_text") val bottomText: OkText? SerializedName("accent_color") val accentColor: OkRGBA? ) : LayoutData() data class ThreeTileAvatarText( SerializedName("top_right_tile_url") val topRightTileUrl: String? SerializedName("avatar_url") val avatarUrl: String?, SerializedName("bottom_text") val bottomText: OkText?, //More layout params... Create layout- based models
  5. Epoxy. A RecyclerView on Steroids. class LayoutAdapter : TypedEpoxyController<DiscoveryState>() {

    fun buildModels(state: DiscoveryState) { for (layoutData in state.layouts) { when (layoutData) { is PictureTitleSubtitle -> addModel(PictureTitleSubtitleModel(layoutData)) is ThreeTileAvatar -> addModel(ThreeTileAvatarModel(layoutData)) is ThreeTileAvatar -> addModel(ThreeTileAvatarModel(layoutData)
  6. But serious, what is state? data class DiscoveryState(val isLoading: Boolean

    = false, val currentPageIndex: Int = 0, val isLoggedIn: Boolean = true, val layouts: List<LayoutData>? = null)
  7. The Action in Interaction. sealed class Action data class NavigateTo(val

    path: String) data class LikeUser(val id:String ) data class BlockUser(val id:String) data class ReportUser(val id:String)
  8. How do we modify state? A reducer. Class DiscoveryReducer :

    Reducer<DiscoveryState> { fun reduce(state: DiscoveryState, action: Action): DiscoveryState { return when (action) { is INITIALIZE_DISCOVERY_STATE -> { DiscoveryState() } is UPDATE_STATUS -> { state.copy(status = action.status) } is GET_LAYOUT_SUCCESS -> { state.copy(layoutData = state.layoutData?.map { if (it.sectionId == action.payload.section.sectionId) action.payload.section else it }) }
  9. What if we’ve got side business? ActionCreators class DiscoveryActionCreator :

    Middleware<DiscoveryState> { private val subscriptions: CompositeSubscription = CompositeSubscription() val actionsObservable: PublishSubject<OkAction> = PublishSubject.create() fun dispatch(store: Store<DiscoveryState>, action: Action, next: NextMiddleware) { when (action) { is GET_LAYOUT_DATA -> { subscriptions += DiscoveryService.getLayoutData() .subscribe({ layouts: List<LayoutData>? -> dispatch(GET_LAYOUT_DATA_SUCCESS(layouts) } }, { e -> dispatch(GET_LAYOUT_DATA_ERROR(e)) })
  10. Bind the action to your view and … <variable type="viewModels.PictureTitleSubtitleViewModel"/>

    <android.support.constraint.ConstraintLayout android:onClick="@{() -> viewModel.emitModelAction()}"> fun emitModelAction() { viewModelActionSubject.onNext(model.action) }