LongCut logo

Building a scalable, modularized, testable app from scratch

By Android Developers

Summary

Topics Covered

  • Unidirectional Data Flow Creates Reactive UI
  • Feature Modules Enable Parallel Development
  • Test Doubles Isolate ViewModel Logic
  • Stateless Composables Simplify UI Testing
  • Material 3 Provides Semantic Color Naming

Full Transcript

[Music] hello, I'm, Don, and, I'm, an, engineer, on, the Android, developer, relations, team, today I'm, going, to, show, you, how, to, build, a

scalable, modularized, testable, app, from scratch, now, this, won't, be, a, Hello, World app, I'll, concentrate, on, building, an, app architecture, which, will, enable, your, app to, grow, we, use, modules, to, separate

responsibility, and, create, a, test infrastructure, to, verify, that, your, app behaves, as expected, lastly, we'll, look, at, how, to build, a, flexible, intuitive, and, greatl looking, user, interface, I'll, be, covering

a, lot, of, different, concepts, so, if, I cover, something, you're, not, familiar, with there's, a, link, on, virtually, every, slide which, will, take, you, to, more, information about, the, current topic, throughout, this, talk, I'll, use, the

now, and, Android, app, as, an, example, the source, code, is, available, on GitHub, in, the, app, we, have, a, screen, which shows, content, which, is, customized, for you, it, displays, a, list, of, news, articles

and, allows, the, user, to, bookmark, each article, we're, going, to, take, a, look, at how, the, foru, feature, was, built, starting with, how, bookmarks, work, the, architecture

for, a, feature, like, this, can, be, broadly divided, into, two, layers, UI, and, data, the data, layer, obtains, and, exposes, app, data to, the, UI, layer, as, well, as, performing

business, Logic, the, UI, layer, displays, app data, on, screen, and, reacts, to, any, changes in, that, data, an, important, word, here, is reacts, by, setting, up, a, stream, of, data

between, the, layers, we, are, creating, a reactive, UI, it, just, needs, to, be, told, how to, update, its, state, when, new, data arrives, it's, important, to, understand

that, the, UI, does, not, request, or, Poll, for data, it, just, collects data, the, only, communication, which happens, in, the, opposite, direction, are events, the, data, layer, handles, these

events, updating, its, underlying, data, and emitting, any, changes, back, to, the, UI in, this, way, we, have, a, single, source, of Truth, for, the, apps, data, and, the, data

only, Flows, In, One, Direction, This, is known, as, unidirectional, data, flow, and, is an, excellent, way, of, keeping, your, app, in a, consistent state, if, we, apply, this, General, guidance

to, this, specific, feature, we, can, clearly Define, the, responsibilities, for, each layer, the, data, layer, should, store, user bookmarks, and, expose, them, to, the, UI layer, by, publishing, a, stream, we, can, do

this, using, cotlin, flows, the, UI, layer then, displays, this, information, and updates, it, whenever, it, changes, the, UI should, also, provide, a, way, for, the, user to, add, and, remove, bookmarks, and, these

user, events, are, passed, back, to, the, data layer, so, the, data, can, be, updated, let's dive, into, the, data, layer, to, see, the individual, classes, which, we, should implement, the, data, layer, architecture

can, be, split, into, two, types, of, component repositories, which, expose, the, app, data and, centralize, changes, to, that, data, and data, sources, which, obtain, data, from, a single, Source, let's, take, this, from, from

Theory, to, Reality, by, implementing, the classes, in, the, data, layer, we'll, start, by implementing, a, local, data, source, class this, will, store, bookmarks, locally, on, the

device, using, jetpack, data, store, let's take, a, look, at, the, code, local, data source, accepts, a, data, store, object, as, a dependency, and, exposes, a, stream, of bookmarks, in, the, bookmark, stream

variable, each, bookmark, is, just, a, string ID, of, the, news, resource, which, is bookmarked, data, store, provides, the underlying, data, stream, it, gives, us, a flow, of, data, which, we, then, convert, to, a

list, of, bookmarks, using, the, map, function the, most, important, concept, to, understand here, is, that, we're, not, pulling, data directly, from, data, store, we're, merely setting, up, the, stream, of, data, and mapping, each, item, in, that, stream, to, a

format, which, clients, of, this, class, can use, in, our, case, a, list, of strings, lastly, it, provides, a, function, to update, bookmarks, by, adding, or, removing them, from, a, map, back, by, data, store, each

time, the, bookmarks, change, a, new, list, of bookmarks, is, emitted, into, the, bookmark Stream, flow we, can, now, add, a, repository, to, expose these, functions, to, the, UI layer, the, code, in, the, bookmarks

repository, is, pretty, simple, it, takes, our local, data, source, as, a, dependency, and exposes, The, Bookmark, stream, and, toggle bookmark, function, from, it, if, we, were doing, something, more, complicated, like

combining, data, from, multiple, data sources, this, would, be, the, place, to, do, it a, common, example, is, an, offline, first feature, which, pulls, data, from, a, network data, source, then, synchronizes, it, with, a

local, data source, we, also, need, some, news, articles to, display, and, obtaining, these, can, be implemented, in, much, the, same, way, the only, difference, being, that, the, data comes, from, the, network, rather, than, being

stored locally, we, can, add, a, network, data, source to, pull, news, articles, from, a, rest, API and, a, news, repository, to, expose, those news, articles, to, the, UI, layer, it's, good

practice, to, have, a, repository, for, each type, of, data, in, your, app, so, that's, our data, layer sorted, now, our, UI, layer, needs, to, display those, bookmarks, next, to, each, news

article, and, let, the, user, add, and, remove bookmarks, let's, take, a, look, at, the, UI layer, in, more detail, here, we, also, have, two, component types, the, state, holder, handles, the

screen, logic, and, constructs, UI, state from, app, data, it, does, this, by, listening for, changes, in, the, data, layer, and, the screen, displays, that, UI, State, using, UI elements, again, applying, this, General

guidance, to, our, feature, we, can, Define the, responsibilities, for, each, layer, and the, classes, we, need, to, create, let's, take a, look, at, the, state, holder, for, this, we can, use, a, jetpack, view, model, pre, fixing

the, name, with, for, you, the, name, of, this feature, this, class, listens, for, data, from the, news, and, bookmarks, repositories, and combines, them, to, create, a, UI, State before, we, dive, into, the, code, let's, look

at, exactly, what's, Happening, Here, we, have lists, of, news, resources, coming, from, our news, repository, and, we, have, lists, of, IDs coming, from, our, bookmarks, repository, our

view, model, combines, these, two, to, create a, list, of, savable, news, resources, each item, in, this, list, contains, the, news resource, itself, and, whether, it's, saved or, not the, code, for, these, models, is, pretty

simple, news, resource, has, some, fields, for the, news, article, like, its, ID, and, title and, savable, news, resource, is, just, a wrapper, around, news, resource, which, also includes, whether, that, news, resource, is

bookmarked, it's, used, by, our, UI, layer, to represent, the, combined, data, from, both the, news, and, bookmarks repositories, and, here's, the, code, for, our view, model, we, accept, the, data

repositories, in, our, Constructor, and create, a, combined, data, variable, to, store a, flow, of, savable, news, resources, this, is is, created, using, the, combine, function which, as, its, name, suggests, combines, the

data, streams, from, our, two repositories, whenever, the, data, changes in, either, stream, our, transformation function, is, called, with, the, latest, news resources, and, bookmarks, inside, it, we, map

each, news, resource, to, a, savable, news resource, checking, to, see, whether, it's bookmarked, let's, go, back, to, our, view model, diagram, for, a, second, once, the, data is, combined, our, view, model, needs, to

convert, this, to, a, UI, state, which, can, be one, of, two, possible, values, loading, or success, loading, is, used, when, data, is being, initially, loaded, usually, when, the news, resources, are, being, loaded, over, the

network, and, success, is, used, when, the data, has, been, loaded, successfully, it contains, the, list, of, savable, news resources, so, the, UI, can, display, them here's, the, code, which, does, that, we, use, a

sealed, interface, to, represent, the, two, UI States, loading, is, just, an, object, whereas success, is, a, class, which, holds, the savable, news resources, in, our, view, model, we, create, a

variable, to, expose, our, UI, State, as, a state, flow, this, this, is, a, flow, which, can have, an, initial, value, and, is, optimized for, sharing, state, to, convert, our combined, data, into, a, UI, State, we, simply

map, the, list, of, savable, news, resources into, the, success, UI state, to, convert, the, combined, data, flow into, a, state, flow, we, use, state, in, we specify, the, co-, routine, scope, which, the

flow, should, be, started, in, and, an, initial value, of loading, collecting, from, flows, can, be expensive, especially, if, they, access, the network, or, sensors, we, can, use, the started, parameter, to, indicate, that, we

only, want, the, flow, to, be, active, when there, is, at, least, one, collector, usually a, screen, this, avoids, wasting resources, when, the, last, collector unsubscribes, we, wait, 5, seconds, before

stopping, the, Upstream, flow, just, in, case the, collector, only, went, away, temporarily as, can, happen, during, a, screen, rotation this, avoids, the, potentially timeconsuming, operation, of, restarting the

flow, the, final, task, our, view, model should, perform, is, to, handle, requests from, the, UI, to, save, and, unsave, bookmarks we, do, this, by, creating, a, bookmark function, inside, it, we, call, the, relevant

method, on, the, bookmarks, repository, and execute, that, call, from, The, View, model scope, now, one, of, the, benefits, of, using, a view, model, is, that, it, survives configuration, changes, this, means, that

operations, initiated, using, the, view model, scope, will, continue, to, run, and, our UI, state, will, be preserved, okay, it's, time, to, display something, on, screen, for, this, we, create

you, guessed, it, a, 4u, screen, this, will read, the, UI, state, from, The, View, model and, render, it, using, UI, elements, we'll use, jetpack, compos, for, this, as, it's, the recommended, UI, framework, for, new

apps, let's, refresh, our, memory, on, what the, design, for, this, feature, looks, like we, can, see, that, the, news, articles, are displayed, in, a, column, and, the, design concept, for, each, article, is, known, as, a

card, so, let's, go, ahead, and, create, a draft, implementation, for, our UI, first, we, create, a, composable, function for, the, news, resource, card, it, takes, the news, resource, its, bookmark, State, and, a

Lambda, which, we, called, when, the, user Taps, on, the, bookmark, button, we, create, a column, which, holds, the, UI, elements, for the, title, content, and, bookmark, button when, the, bookmark, button, is, tapped, we

call, on, toggle bookmark, we, also, create, a, composable, to display, a, list, of, these, news, resource cards, called, news, feed, it, simply, creates a, column, and, iterates, through, the

elements, rendering, a, news, resource, card for, each one, now, you, can, preview, composes, in Android, Studio, using, the, app, preview annotation, here's, how, our, news, feed looks

currently, okay, it, isn't, going, to, win, any Design, Awards, and, we're, certainly, not matching, the, design, spec, yet, but, don't worry, we're, going, to, fix, this shortly, now, let's, create, the, for, you screen, this, accepts, the, current, UI, State

and, a, Lambda, to, call, when, the, user, Taps on, any, of, the, bookmark, buttons, when, the UI, state, is, loading, we, just, display, the text, loading, and, for, the, success, State we, get, the, news, resources, from, the, UI

State, and, pass, them, to, the, news, feed along, with, the, Lambda, to, be, called, when the, user, adds, or, removes, a, bookmark, so how, do, we, get, our, UI, St, State, well, we can, create, a, stateful, version, of, foru

screen, which, obtains, our, view, model, and once, we, have, it, we, can, use, collect, a state, with, life, cycle, to, collect, from the, stream, only, when, the, app, is, in, the foreground, to, avoid, wasting, resources

and, to, convert, the, stream, into, a composed, State, when, the, UI, State, changes it, causes, any, composable, functions, which is, Reddit, like, our, for, you, screen, to, be

recomposed, once, we, have, the, UI, State, we simply, Supply, it, to, our, state, list, for you, screen, along, with, a, reference, to, any events, which, the, screen, needs, to, call, in our, case, just, the, toggle, bookmark

function, so, why, don't, we, just, have, a single, stateful, for, you, screen, well, as we'll, see, later, stateless, functions, are easier, to, test, and, to

preview, in, a, compose, app, we, still, need an, activity, this, is, the, glue, between, the Android, framework, and, your, compose, code and, is, what, shows, your, UI, to, the, user, we try, to, keep, our, activity, as, simple, as

possible, just, calling, set, content, and then, our, UI, code Okay, so, we've, created, classes, for, the uui, and, data, layers, we, could, just, place all, these, classes, inside, the, app, module

but, we, know, that, our, app, is, going, to, do more, than, just, display, news, articles, by creating, modules, for, each, distinct, set of, responsibilities, we, can, make, our codebase, more, scalable, modules, can, also

enable, parallel, working, and, potentially reduce, build times, here's, the, modularization, strategy we, used, in, the, now, and, Android, app, at the, top, level, we, have, app, modules, which are, responsible, for, bringing, features

together, an, app, module, exists, for, every app, which, can, be, built, as, part, of, the project, the, mobile, app, is, the, most obvious, of, these, but, we, also, have, an, app module, for, displaying, the, visual, design system, and, could, have, more, app, modules

for, different, Target, platforms, such, as wear, or TV, app, modules, depend, on, feature, modules these, are, scoped, to, a, single, area, of user-facing, responsibility, such, as providing, the, content, for, the, for, you

screen, feature, modules, do, not, depend, on each, other, but, can, depend, on, core modules, these, are, common, Library, modules used, throughout, the, app, also, scoped, to, a single, area, of, responsibility, for

example, repositories, are, held, in, the data, module, and, networking, code, in, the network, module, let's, figure, out, where our, classes, should, go, our, main, activity goes, into, our, app, module, classes, which

are, specific, to, the, for, you, feature, are moved, into, the, for, you, feature, module data, classes, which, are, used, by, other modules, are, moved, into, the, core, model module, repository, trees, into, core, data

classes, associated, with, local, storage using, data, store, move, into, core, data store and, network, classes, move, into, core Network, now, a, common, problem, with modularization, is, that, you, end, up, with

lots, of, duplicate, dependencies, and configuration, blocks, inside, your, build files, we, solve, this, in, now, on, Android using, a, feature, called, convention plugins, the, idea, is, to, move, common

options, and, dependencies, into, a, plug-in and, then, include, that, plugin, in, all, the modules, which, require, those, options, this way, all, your, configuration, stays, in, one place, and, can, be, easily

updated, the, plugins, and, supporting, code is, contained, within, a, separate, build logic, module, which, is, built, before, any other, module, this, ensures, that, any configuration, changes, are, propagated, to

all, dependent, modules, before, they're built, another, common, problem, in, modular code, bases, is, keeping, track, of, Library versions, especially, when, they're scattered, through, multiple, gradal, files

version, cataloges, is, a, gradal, feature which, solves, this, a, version, catalog, is, a single, file, which, contains, a, list, of every, Library, used, in, the, app, and, its version, each, library, has, a, unique, ID

which, can, be, referenced, in, gradal configuration, files, whenever, we, need, to update, the, library, version, we, just update, it, in, the, version catalog, this, modularization, strategy helped, us, to, build, a, Loosely, coupled

graph, of, modules, for, now, an, Android enabling, us, to, work, in, parallel, and reduce, the, amount, of, time, deciding, where to, put, new, code, you, don't, have, to modularize, from, the, outset, but, if, you know, your, project, needs, to, scale, you, can

save, a, lot, of, time, by, introducing, a modularization, strategy early, so, at, this, point, we, know, what classes, to, create, and, which, modules, to save, them, in, what, we, don't, know, for, sure

is, whether, our, code, functions, as intended, tests, can, do, this, by, verifying the, behavior, of, individual, classes, and our, app, as, a, whole, they, allow, us, to, add new, features, or, refactor, our, code, base with, the, confidence, that, if, we, break

something, we'll, know, about, it, before, our product, is, shipped, to, users, in, fact, if you, have, a, clear, idea, of, the, classes, you need, to, create, it's, even, better, to, write the, tests, first, but, it's, up, to, you, to

decide, when, to, write, tests, just, as, long as, you, actually, write, them there's, a, lot, of, theory, behind, testing which, you, can, check, out, using, the, link below, I'm, just, going, to, show, you, how, we

can, write, a, unit, test, and, a, UI, test, to verify, the, behavior, for, our, feature, we should, add, test, wherever, there, is, logic and, one, of, the, places, there's, a, lot, of logic, is, in, our, view

model, as, we've, seen, already, it, combines data, from, two, streams, and, converts, it into, UI, State, let's, write, a, unit, test, to verify, this Behavior, the, first, thing, we, need, to, do

when, writing, a, test, is, isolate, the, test subject, from, its, dependencies, in, this case, the, repositories, instead, of, using the, real, repositories, which, are, going, to be, performing, long, running, operations like, pulling, data, from, the, Network, and

local, storage, we, should, instead, create test, dependencies, also, known, as, test doubles, this, allows, us, to, control, the inputs, to, our, test, subject, and, verify the, output, plus, our, tests, will, run, a, lot

faster, the, problem, is, our, view, model depends, on, concrete, classes, we, should rename, any, concrete, classes, and, have them, Implement, an, interface, which, the test, subject, can, then, depend, on

this, way, we, can, create, test, classes which, will, be, supplied, when, testing let's, Implement, our, view, model, test class, we, start, by, creating, a, rule, which sets, our, co-routine, dispatcher, to, a, test

dispatcher, this, ensures, that, any co-routines, are, executed, sequentially inside, our, test, and, is, essential, when testing flows, then, we, set, up, the, dependencies and, create, a, variable, to, hold, the

subject, under, test, the, view, model, and, we ensure, that, it's, recreated, with, our, test dependencies, before, each, test, is run, now, we, can, write, a, test, which, will verify, that, after, the, news, resources, and

bookmarks, are, loaded, the, view, model produces, the, correct, UI, State, inside, our test, we, start, collecting, from, The, View model, State, without, this, no, values, would

be, emitted, into, the, flow, we, tell, our test, news, repository, to, send, some, news resources, to, the, view, model, and, our, test bookmarks, repository, to, toggle, a bookmark, for, the, first, news

resource, now, we, get, our, UI, State, we, then check, that, it's, success, and, the, correct news, resource, has, been, booked, marked lastly, we, cancel, the, collection, job otherwise, the, test, will, run

indefinitely, since, this, is, a, locally, run unit, test, we, save, it, in, the, test, folder in, our, feature module, now, let's, create, a, test, for, our screen, in, a, UI

test, here, we, have, a, choice, we, can, either run, the, UI, test, directly, on, our, host using, a, testing, framework, like, Robo electric, this, is, known, as, a, local, test and, is, saved, in, the, same, location, as, our previous, test, or, we, can, run, it, on, a

device, or, emulator, running, Android, known as, is, an, instrumented, tests, local, tests generally, run, faster, but, offer, less Fidelity, than, instrumented, tests, let's create, an, instrumented, test, for, our

screen, first, we, add, a, junit, rule, which allows, us, to, control, composes, now, we create, a, test, which, will, verify, that, the news, articles, are, displayed, the, set content, method, allows, us, to, supply, a

composable, and, have, it, rendered, on screen, exactly, the, same, way, as, it, works in, our, main activity, inside, here, we, create, our, test subject, the, for, you, screen, and, pass, in the, success, state, and, some, sample, news

resources, here, we, can, see, the, benefit, of having, a, stateless, screen, composable, it makes, it, really, easy, to, test, after setting, up, our, screen, we, can, use, the, on node, with, text, matcher, to, find, a, node, in

the, composed, graph, with, the, title, of, our news, resource, and, verify, that, it, exists since, this, is, an, instrumented, test, we save, it, in, the, Android, test folder, now, that, we, know, that, everything

is, working, as, intended, and, we, have, a solid, infrastructure, in, place, let's, make our, app, look better, the, material, design, system provides, a, framework, and, set, of guidelines, for, creating, compelling

consistent, and, intuitive, user, interfaces it, includes, a, set, of, customizable, UI components, for, Jetpack, comp, POS, material 3, is, the, latest, version, of, material design, and, our, design, team, has, given, us

a, beautiful, design, spec, based, on, this including, colors, and, typography, we, can use, these, to, define, a, theme, for, our, app any, material, design, components, we, use will, be, styled, according, to, our, theme

let's, take, a, look, at, this, in action, we, take, the, colors, from, our design, design, spec, and, Define, them, in code, then, we, Define, a, color, scheme, for light, and, dark, modes, material, 3, gives

each, color, a, semantic, name, which describes, the, context, in, which, that color, should, be, used, for, example, the, on primary, color, will, be, used, when, on, top of, the, primary, color, we, can, also, create

a, typography, class, to, specify, our, text styles, again, material, 3, gives, us, a, set of, names, to, clearly, define, the, role, for each, textile, with, our, colors, and, tex Styles, defined, we, can, now, create, a, theme

composable, which, wraps, all, the, content in, our, app, first, we, decide, which, color scheme, to, use, based, on, whether, the device, is, in, dark, mode, then, create, a material, theme, based, on, our, color, scheme and

typography, to, use, the, theme, we, simply wrap, our, content, in, our, theme composable, if, we, preview, our, news resource, card, we, can, see, that, the, button is, now, using, the, colors, we, defined, in our

theme, we, can, specify, our, text, Styles whenever, we, render, text, and, we, can, take advantage, of, other, material, design components, like, the, card, which, adds rounded, Corners, by, default, and, derives colors, from, our

theme, one, of, the, great, things, about material, design, components, is, that they're, designed, to, be, customized, in, now in, Android, we, customized, several, of, the stock, components, and, created, a, design catalog, app, which, ships, as, part, of, the

project, so, everyone, on, the, team, can easily, see, the, UI, components, they, have to, work, with, a, good, example, is, the toggle, button, class, it, wraps, the material, icon, button, to, provide, extra

features, like, the, circular, background you, see, when, the, button, is, toggled, on bookmark, button, then, wraps, toggle, button to, provide, the, book, Mark, icons, for, the on, and, off

States, now, if, we, swap, our, normal, button for, the, bookmark, button, add, some padding, and, an, image, using, an, image loading, Library, like, coil, will, start, to

get, much, closer, to, our, design spec, okay, we, made, it, we, learned, how, to implement, a, scalable, architecture, and modularize, our, code, base, as, well, as, our tests, to, verify, our, apps, Behavior, lastly

we, learned, how, to, use, material, design with, jetpack, compos, to, create, beautiful intuitive, apps, but, wait, this, talk, only covers, the, very, basics, of, these, topics and, there's, some, important, ones, which, I haven't, covered, which, you, should

definitely, check, out, including navigation, dependency, injection, using Hilt, performance, testing, and, designing for, all, screen, sizes, the, now, and, Android app, includes, all, of, these, features, so, be

sure, to, check, it, out, thanks, for, watching [Music]

Loading...

Loading video analysis...