~5 years • Selling train tickets for ~3.5 years • Passionate about building scalable app architectures • Outside of work I like space exploration, chess, Man City, and UFC A bit about me
~4 years • Selling train tickets for ~2.5 years • Passionate about building scalable app architectures • Outside of work I like space exploration, chess, Man City, and UFC A bit about me
• What is Molecule? • A brief intro to writing Molecule code • How Molecule fits into our traditional Android presentation architectures • What real-world problems does it solve? • Simplifying the logic expansion of the ViewModel • Combining flows with compose-style declarative code • Building reusable state management for reusable UI components
you already use) “It [Molecule] allows you to use the compiler magic of Compose for the pure manipulation of data… just aggregating inputs and emitting outputs.”
by remember { mutableIntStateOf(0) } Text( text = "$count", style = MaterialTheme.typography.titleLarge, color = Color(0xFFCB2A7B), fontSize = 144.sp, modifier = modifier ) LaunchedEffect(Unit) { while (true) { delay(1000) count++ } } } Let’s look at the most simple example Imagine we want to count seconds
@Composable fun Profile() { val user by userFlow.collectAsState(null) val balance by balanceFlow.collectAsState(0L) if (user == null) { Text("Loading…") } else { Text("${user.name} - $balance") } }
@Composable fun Profile() { val user by userFlow.collectAsState(null) val balance by balanceFlow.collectAsState(0L) if (user == null) { Text("Loading…") } else { Text("${user.name} - $balance") } }
@Composable fun Profile() { val user by userFlow.collectAsState(null) val balance by balanceFlow.collectAsState(0L) if (user == null) { Text("Loading…") } else { Text("${user.name} - $balance") } }
val user by userFlow.collectAsState(null) val balance by balanceFlow.collectAsState(0L) return if (user == null) { ProfileModel.Loading } else { ProfileModel.Data(user.name, balance) } } val userFlow = db.userObservable() val balanceFlow = db.balanceObservable() val models: StateFlow<ProfileModel> = scope.launchMolecule(mode = ContextClock) { profilePresenter(userFlow, balanceFlow) } Another example
val user by userFlow.collectAsState(null) val balance by balanceFlow.collectAsState(0L) return if (user == null) { ProfileModel.Loading } else { ProfileModel.Data(user.name, balance) } } val userFlow = db.userObservable() val balanceFlow = db.balanceObservable() val models: StateFlow<ProfileModel> = scope.launchMolecule(mode = ContextClock) { profilePresenter(userFlow, balanceFlow) } Another example
val user by userFlow.collectAsState(null) val balance by balanceFlow.collectAsState(0L) return if (user == null) { ProfileModel.Loading } else { ProfileModel.Data(user.name, balance) } } val userFlow = db.userObservable() val balanceFlow = db.balanceObservable() val models: StateFlow<ProfileModel> = scope.launchMolecule(mode = ContextClock) { profilePresenter(userFlow, balanceFlow) } Another example
val user by userFlow.collectAsState(null) val balance by balanceFlow.collectAsState(0L) return if (user == null) { ProfileModel.Loading } else { ProfileModel.Data(user.name, balance) } } val userFlow = db.userObservable() val balanceFlow = db.balanceObservable() val models: StateFlow<ProfileModel> = scope.launchMolecule(mode = ContextClock) { profilePresenter(userFlow, balanceFlow) } Another example
other fl ow testing methodology) @Test fun `GIVEN randomise event is received THEN new player data is loaded`() = runTest { val input = flowOf(RandomiseClicked, RandomiseClicked) ... moleculeFlow(RecompositionMode.Immediate) { sut.present(events = input) }.test { assertEquals(PlayerComponentState.Loading, awaitItem()) assertEquals(fakePlayer1InfoState, awaitItem()) assertEquals(PlayerComponentState.Loading, awaitItem()) assertEquals(fakePlayer2InfoState, awaitItem()) assertEquals(PlayerComponentState.Loading, awaitItem()) assertEquals(fakePlayer3InfoState, awaitItem()) } }
for non-ui purposes • It allows us to write compose for our presentation logic • It’s fun! • Using the Molecule ViewModel we can create VMs that scale much better than more traditional methods • It lets us combine multiple streams of data in a readable and imperative style • It’s really great for decoupling the logic for a reusable ui component from the ViewModel of each screen