Skip to content

Commit c25ab2c

Browse files
authored
feat!: remove coroutine dependency when building cache
1 parent 7b8866c commit c25ab2c

23 files changed

Lines changed: 189 additions & 242 deletions

.github/workflows/pull_request.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,6 @@ jobs:
1414
with:
1515
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
1616
github_configuration_owner: 'krogerco'
17-
github_configuration_repo: 'Shared-CI-Action-Android'
17+
github_configuration_repo: 'Shared-CI-Workflow-Android'
1818
github_configuration_path: '.github/config/pr-title-checker-config.json'
1919
github_configuration_ref: 'v1.0.0'

README.md

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,26 +37,27 @@ val fileCache = SnapshotFileCacheBuilder.from(
3737
filename = "cacheFile.json", // cache file created in application's cache directory
3838
keySerializer = String.serializer(), // serializer for cache keys
3939
valueSerializer = String.serializer(), // serializer for cache values
40-
).build().getOrThrow()
40+
).build()
4141
```
4242

4343
Once the `fileCache` is created a `MemoryCacheManager` can then be created that utilizes it.
4444
```kotlin
4545
cache = MemoryCacheManagerBuilder.from(
4646
context, // application context
47-
scope, // CoroutineScope used to periodically save the cache
4847
fileCache, // persistent cache to periodically save cache entries to
49-
).build()
48+
)
49+
.coroutineScope(coroutineScope) // CoroutineScope used to periodically save the cache
50+
.build()
5051
```
5152

5253
Use the `cache`.
5354

5455
```kotlin
5556
// add a value to the cache
56-
cache["newKey"] = computeExpensiveValue()
57+
cache.put("newKey", computeExpensiveValue())
5758

5859
// ...somewhere else retrieve and use the value
59-
val expensiveValue = cache["newKey"]
60+
val expensiveValue = cache.get("newKey")
6061
println(expensiveValue)
6162
```
6263

@@ -107,9 +108,10 @@ A `MemoryCacheManager` can be created using one of the builder factory functions
107108
```kotlin
108109
val cache = MemoryCacheManagerBuilder.from(
109110
context, // application context
110-
coroutineScope,
111111
snapshotPersistentCache = fileCache, // see Cache Configuration section for an example
112-
).build()
112+
)
113+
.coroutineScope(coroutineScope)
114+
.build()
113115
```
114116

115117
When retrieving an entry from the `Cache` an expired entry will never be returned.

android/src/main/java/com/kroger/cache/android/extensions/MemoryCacheManagerBuilderExtensions.kt

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ import com.kroger.cache.MemoryCacheManagerBuilder
2828
import com.kroger.cache.SnapshotPersistentCache
2929
import com.kroger.cache.android.internal.AndroidMemoryLevelNotifier
3030
import com.kroger.cache.internal.CacheEntry
31-
import kotlinx.coroutines.CoroutineScope
3231

3332
/**
3433
* Creates a [MemoryCacheManagerBuilder] that uses [AndroidMemoryLevelNotifier] to trim
@@ -37,8 +36,6 @@ import kotlinx.coroutines.CoroutineScope
3736
*/
3837
public fun <K, V> MemoryCacheManagerBuilder.Companion.from(
3938
context: Context,
40-
coroutineScope: CoroutineScope,
41-
snapshotPersistentCache: SnapshotPersistentCache<List<CacheEntry<K, V>>>?,
42-
): MemoryCacheManagerBuilder<K, V> =
43-
from(coroutineScope, snapshotPersistentCache)
44-
.memoryLevelNotifier(AndroidMemoryLevelNotifier(context))
39+
snapshotPersistentCache: SnapshotPersistentCache<List<CacheEntry<K, V>>>? = null,
40+
): MemoryCacheManagerBuilder<K, V> = from(snapshotPersistentCache)
41+
.memoryLevelNotifier(AndroidMemoryLevelNotifier(context))

android/src/test/java/com/kroger/cache/android/extensions/SnapshotFileCacheBuilderExtensionsTest.kt

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,10 @@
2424
package com.kroger.cache.android.extensions
2525

2626
import android.content.Context
27-
import com.google.common.truth.Truth.assertThat
2827
import com.kroger.cache.SnapshotFileCacheBuilder
2928
import io.mockk.every
3029
import io.mockk.mockk
3130
import io.mockk.verify
32-
import kotlinx.coroutines.runBlocking
3331
import kotlinx.serialization.builtins.serializer
3432
import org.junit.jupiter.api.Test
3533
import org.junit.jupiter.api.io.TempDir
@@ -40,32 +38,30 @@ internal class SnapshotFileCacheBuilderExtensionsTest {
4038
private lateinit var tempDir: File
4139

4240
@Test
43-
fun whenGenericTypeBuilderUsedThenCacheCreatedSuccessfully() = runBlocking {
41+
fun whenGenericTypeBuilderUsedThenCacheCreatedSuccessfully() {
4442
val context = mockk<Context>()
4543
every { context.cacheDir } returns tempDir
4644

47-
val cacheResult = SnapshotFileCacheBuilder.from(
45+
SnapshotFileCacheBuilder.from(
4846
context,
4947
"com.kroger.cache.android.test",
5048
{ "" },
5149
{ it.orEmpty().encodeToByteArray() },
5250
).build()
53-
assertThat(cacheResult.isSuccess).isTrue()
5451
verify(exactly = 1) { context.cacheDir }
5552
}
5653

5754
@Test
58-
fun whenGenericCacheEntryBuilderUsedThenCacheCreatedSuccessfully() = runBlocking {
55+
fun whenGenericCacheEntryBuilderUsedThenCacheCreatedSuccessfully() {
5956
val context = mockk<Context>()
6057
every { context.cacheDir } returns tempDir
61-
val cacheResult = SnapshotFileCacheBuilder.from(
58+
SnapshotFileCacheBuilder.from(
6259
context,
6360
"com.kroger.cache.android.test",
6461
String.serializer(),
6562
String.serializer(),
6663
).build()
6764

68-
assertThat(cacheResult.isSuccess).isTrue()
6965
verify(exactly = 1) { context.cacheDir }
7066
}
7167
}

cache/src/main/java/com/kroger/cache/Cache.kt

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -30,42 +30,29 @@ public interface Cache<K, V> {
3030
/**
3131
* @return the value corresponding with the given [key], or null if the key is not present or the value is expired.
3232
*/
33-
public fun get(key: K): V?
33+
public suspend fun get(key: K): V?
3434

3535
/**
3636
* Associates the given [key] with the given [value].
3737
* If [key] is already in the [Cache] the value will be overwritten by [value].
3838
*/
39-
public fun put(key: K, value: V)
39+
public suspend fun put(key: K, value: V)
4040

4141
/**
4242
* Each pair will be inserted into the cache associating the [Pair.first] element with [Pair.second].
4343
* If [Pair.first] is already in the [Cache] the value will be overwritten by [Pair.second].
4444
*
4545
* @param pairs the key value pairs to insert into the cache.
4646
*/
47-
public fun putAll(pairs: Iterable<Pair<K, V>>)
47+
public suspend fun putAll(pairs: Iterable<Pair<K, V>>)
4848

4949
/**
5050
* Removes the entry for [key] from the [Cache].
5151
*/
52-
public fun remove(key: K)
52+
public suspend fun remove(key: K)
5353

5454
/**
5555
* Removes all entries from the [Cache].
5656
*/
57-
public fun clear()
57+
public suspend fun clear()
5858
}
59-
60-
/**
61-
* @see [Cache.put]
62-
*/
63-
public operator fun <K, V> Cache<K, V>.set(key: K, value: V) {
64-
put(key, value)
65-
}
66-
67-
/**
68-
* @see [Cache.get]
69-
*/
70-
public operator fun <K, V> Cache<K, V>.get(key: K): V? =
71-
get(key)

cache/src/main/java/com/kroger/cache/MemoryCacheManagerBuilder.kt

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,10 @@ import kotlin.time.Duration
3333
/**
3434
* Builder for a thread safe [Cache] that internally manages a [MemoryCache][com.kroger.cache.internal.MemoryCache].
3535
* The cache will periodically persist its data to a [SnapshotPersistentCache] and will enforce the given [CachePolicy].
36+
* Once the [Cache] is no longer in use its [coroutineScope] should be cancelled.
3637
*/
3738
public interface MemoryCacheManagerBuilder<K, V> {
38-
public suspend fun build(): Cache<K, V>
39+
public fun build(): Cache<K, V>
3940

4041
/**
4142
* The [CachePolicy] to apply to the [MemoryCache][com.kroger.cache.internal.MemoryCache] and its entries.
@@ -69,21 +70,25 @@ public interface MemoryCacheManagerBuilder<K, V> {
6970
*/
7071
public fun dispatcher(dispatcher: CoroutineDispatcher): MemoryCacheManagerBuilder<K, V>
7172

73+
/**
74+
* It is important that the provided [coroutineScope] live as long as the
75+
* [MemoryCache][com.kroger.cache.internal.MemoryCache] is in use. Once the [coroutineScope]
76+
* is cancelled no further changes to the [Cache] will be saved to the [SnapshotPersistentCache].
77+
*
78+
* @param coroutineScope scope to use when periodically updating the [SnapshotPersistentCache].
79+
*/
80+
public fun coroutineScope(coroutineScope: CoroutineScope): MemoryCacheManagerBuilder<K, V>
81+
7282
public companion object {
7383
/**
74-
* Creates a [MemoryCacheManagerBuilder] using the given [coroutineScope] and [snapshotPersistentCache].
75-
* It is important that the provided [coroutineScope] live as long as the
76-
* [MemoryCache][com.kroger.cache.internal.MemoryCache] is in use. Once the [coroutineScope]
77-
* is cancelled no further changes to the [Cache] will be saved to the [snapshotPersistentCache].
84+
* Creates a [MemoryCacheManagerBuilder] using the given [snapshotPersistentCache].
7885
*
79-
* @param coroutineScope scope to use when periodically updating the [snapshotPersistentCache].
8086
* @param snapshotPersistentCache [SnapshotPersistentCache] where entries are periodically saved
8187
*/
8288
public fun <K, V> from(
83-
coroutineScope: CoroutineScope,
84-
snapshotPersistentCache: SnapshotPersistentCache<List<CacheEntry<K, V>>>?,
89+
snapshotPersistentCache: SnapshotPersistentCache<List<CacheEntry<K, V>>>? = null,
8590
): MemoryCacheManagerBuilder<K, V> =
86-
RealMemoryCacheManagerBuilder(coroutineScope, snapshotPersistentCache) {
91+
RealMemoryCacheManagerBuilder(snapshotPersistentCache) {
8792
System.currentTimeMillis()
8893
}
8994
}

cache/src/main/java/com/kroger/cache/SnapshotFileCacheBuilder.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ import java.io.File
4040
* For any single [File] only one [SnapshotPersistentCache] should exist at a time.
4141
*/
4242
public interface SnapshotFileCacheBuilder<T> {
43-
public suspend fun build(): Result<SnapshotPersistentCache<T>>
43+
public fun build(): SnapshotPersistentCache<T>
4444

4545
/**
4646
* The [CoroutineDispatcher] to use when doing [File] I/O.

cache/src/main/java/com/kroger/cache/internal/MemoryCache.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,23 +44,23 @@ internal class MemoryCache<K, V>(
4444
constructor(initialCapacity: Int = DEFAULT_INITIAL_CAPACITY) :
4545
this(initialCapacity, false)
4646

47-
override fun get(key: K): V? {
47+
override suspend fun get(key: K): V? {
4848
return cacheMap[key]
4949
}
5050

51-
override fun put(key: K, value: V) {
51+
override suspend fun put(key: K, value: V) {
5252
cacheMap[key] = value
5353
}
5454

55-
override fun putAll(pairs: Iterable<Pair<K, V>>) {
55+
override suspend fun putAll(pairs: Iterable<Pair<K, V>>) {
5656
cacheMap.putAll(pairs)
5757
}
5858

59-
override fun remove(key: K) {
59+
override suspend fun remove(key: K) {
6060
cacheMap.remove(key)
6161
}
6262

63-
override fun clear() {
63+
override suspend fun clear() {
6464
cacheMap.clear()
6565
}
6666

0 commit comments

Comments
 (0)