This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class LocationActivity : AppCompatActivity() { | |
// Coroutine listening for Locations | |
private var locationUpdatesJob: Job? = null | |
override fun onStart() { | |
super.onStart() | |
locationUpdatesJob = lifecycleScope.launch { | |
locationProvider.locationFlow().collect { | |
// New location! Update the map |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Implementation of a cold flow backed by a Channel that sends Location updates | |
fun FusedLocationProviderClient.locationFlow() = callbackFlow<Location> { | |
val callback = object : LocationCallback() { | |
override fun onLocationResult(result: LocationResult?) { | |
result ?: return | |
try { offer(result.lastLocation) } catch(e: Exception) {} | |
} | |
} | |
requestLocationUpdates(createLocationRequest(), callback, Looper.getMainLooper()) | |
.addOnFailureListener { e -> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class TransactionsRepository( | |
private val defaultDispatcher: CoroutineDispatcher = Dispatchers.Default | |
) { | |
// Mutex protecting the cache mutable state | |
private val cacheMutex = Mutex() | |
private val transactionsCache = mutableMapOf<User, List<Transaction>() | |
private suspend fun addTransaction(user: User, transaction: Transaction) = | |
withContext(defaultDispatcher) { |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class TransactionsRepository( | |
private val defaultDispatcher: CoroutineDispatcher = Dispatchers.Default | |
) { | |
private val transactionsCache = mutableMapOf<User, List<Transaction>() | |
private suspend fun addTransaction(user: User, transaction: Transaction) = | |
// CAREFUL! Access to the cache is not protected. | |
// Concurrency bugs can happen: threads can see stale data | |
// and race conditions may occur. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Module | |
@InstallIn(ViewModelComponent::class) | |
object UserAuthModule { | |
@Provides | |
fun provideValidateUsernameUseCase( | |
userInputAuthData: UserInputAuthData, // scoped to ViewModelComponent | |
repository: UserRepository | |
): ValidateUsernameUseCase { | |
return ValidateUsernameUseCaseImpl(userInputAuthData, repository) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@ViewModelScoped // Scopes type to the ViewModel | |
class UserInputAuthData( | |
private val handle: SavedStateHandle // Default binding in ViewModelComponent | |
) { /* Cached data and logic here */ } | |
class RegistrationViewModel( | |
private val userInputAuthData: UserInputAuthData, | |
private val validateUsernameUseCase: ValidateUsernameUseCase, | |
private val validatePasswordUseCase: ValidatePasswordUseCase | |
) : ViewModel() { /* ... */ } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
val FusedLocationProviderClient.locationFlow() = callbackFlow<Location> { | |
... | |
}.shareIn( | |
// Make the flow follow the applicationScope | |
applicationScope, | |
// Emit the last emitted element to new collectors | |
replay = 1, | |
// Keep the producer active while there are active subscribers | |
started = SharingStarted.WhileSubscribed() | |
) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public suspend fun ProducerScope<*>.awaitClose(block: () -> Unit = {}) { | |
... | |
try { | |
// Suspend the coroutine with a cancellable continuation | |
suspendCancellableCoroutine<Unit> { cont -> | |
// Suspend forever and resume the coroutine successfully only | |
// when the Flow/Channel is closed | |
invokeOnClose { cont.resume(Unit) } | |
} | |
} finally { |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Send location updates to the consumer | |
fun FusedLocationProviderClient.locationFlow() = callbackFlow<Location> { | |
// A new Flow is created. This code executes in a coroutine! | |
// 1. Create callback and add elements into the flow | |
val callback = object : LocationCallback() { | |
override fun onLocationResult(result: LocationResult?) { | |
result ?: return // Ignore null responses | |
for (location in result.locations) { | |
try { |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public suspend inline fun <T> suspendCancellableCoroutine( | |
crossinline block: (CancellableContinuation<T>) -> Unit | |
): T = | |
// Get the Continuation object of the coroutine that it's running this suspend function | |
suspendCoroutineUninterceptedOrReturn { uCont -> | |
// Take over the control of the coroutine. The Continuation's been | |
// intercepted and it follows the CancellableContinuationImpl lifecycle now | |
val cancellable = CancellableContinuationImpl(uCont.intercepted(), ...) | |
/* ... */ |