Skip to content

Instantly share code, notes, and snippets.

View objcode's full-sized avatar

Sean McQuillan objcode

  • Mountain View, CA
View GitHub Profile
@objcode
objcode / UseAfterPrevious.kt
Created May 21, 2019 02:12
Use afterPrevious to ensure one sort runs at a time.
// Solution #2: Add a Mutex
// Note: This is not optimal for the specific use case of sorting
// or filtering but is a good pattern for network saves.
class ProductsRepository(val productsDao: ProductsDao, val productsApi: ProductsService) {
val singleRunner = SingleRunner()
suspend fun loadSortedProducts(ascending: Boolean): List<ProductListing> {
// wait for the previous sort to complete before starting a new one
@objcode
objcode / cancelPreviousThenRun.kt
Last active May 21, 2019 08:00
Example version of cancelPreviosThenRun.
// see the complete implementation at
// https://gist.github.com/objcode/7ab4e7b1df8acd88696cb0ccecad16f7
suspend fun cancelPreviousThenRun(block: suspend () -> T): T {
// If there is an activeTask, cancel it because it's result is no longer needed
activeTask?.cancelAndJoin()
// ...
@objcode
objcode / CancelPrevious.kt
Created May 21, 2019 02:03
Use cancelPreviousThenRun to control concurrency
// Solution #1: Cancel previous work
// This is a great solution for tasks like sorting and filtering that
// can be cancelled if a new request comes in.
class ProductsRepository(val productsDao: ProductsDao, val productsApi: ProductsService) {
var controlledRunner = ControlledRunner<List<ProductListing>>()
suspend fun loadSortedProducts(ascending: Boolean): List<ProductListing> {
// cancel the previous sorts before starting a new one
@objcode
objcode / Solution0.kt
Last active May 23, 2019 09:52
Disable the buttons while a sort is running
// Solution 0: Disable the sort buttons when any sort is running
class ProductsViewModel(val productsRepository: ProductsRepository): ViewModel() {
private val _sortedProducts = MutableLiveData<List<ProductListing>>()
val sortedProducts: LiveData<List<ProductListing>> = _sortedProducts
private val _sortButtonsEnabled = MutableLiveData<Boolean>()
val sortButtonsEnabled: LiveData<Boolean> = _sortButtonsEnabled
init {
@objcode
objcode / Database.kt
Created May 21, 2019 01:50
Implement a one shot request with coroutines (@dao)
@Dao
interface ProductsDao {
// Because this is marked suspend, Room will use it's own dispatcher
// to run this query in a main-safe way.
@Query("select * from ProductListing ORDER BY dateStocked ASC")
suspend fun loadProductsByDateStockedAscending(): List<ProductListing>
// Because this is marked suspend, Room will use it's own dispatcher
// to run this query in a main-safe way.
@Query("select * from ProductListing ORDER BY dateStocked DESC")
@objcode
objcode / Repository.kt
Created May 21, 2019 01:47
Implement a one shot request with coroutines (Repository)
class ProductsRepository(val productsDao: ProductsDao) {
/**
* This is a "regular" suspending function, which means the caller must
* be in a coroutine. The repository is not responsible for starting or
* stopping coroutines since it doesn't have a natural lifecycle to cancel
* unnecessary work.
*
* This *may* be called from Dispatchers.Main and is main-safe because
* Room will take care of main-safety for us.
@objcode
objcode / ViewModel.kt
Last active May 23, 2019 09:52
Implement a one shot request (ViewModel)
class ProductsViewModel(val productsRepository: ProductsRepository): ViewModel() {
private val _sortedProducts = MutableLiveData<List<ProductListing>>()
val sortedProducts: LiveData<List<ProductListing>> = _sortedProducts
/**
* Called by the UI when the user clicks the appropriate sort button
*/
fun onSortAscending() = sortPricesBy(ascending = true)
fun onSortDescending() = sortPricesBy(ascending = false)
@objcode
objcode / ConcurrencyHelpers.kt
Last active May 2, 2024 08:05
Helpers to control concurrency for one shot requests using Kotlin coroutines.
/* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
package com.example.android.gdgfinder.libs
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.launch
class RequestLimiter<R>(scope: CoroutineScope) {
val requestChannel = Channel<Pair<suspend () -> R, CompletableDeferred<R>>>()
private class DownloadActor(val scope: CoroutineScope) {
private val requested = mutableMapOf<Location, MutableList<Reference>>()
private val apiThread = newFixedThreadPoolContext(1, "DownloadActor")
private fun downloader(
references: ReceiveChannel<Reference>,
locations: SendChannel<Location>
) = scope.launch(apiThread) {
for (ref in references) {