17
17
package com.google.firebase.sessions.settings
18
18
19
19
import android.util.Log
20
+ import androidx.annotation.VisibleForTesting
20
21
import androidx.datastore.core.DataStore
22
+ import com.google.firebase.annotations.concurrent.Background
21
23
import com.google.firebase.sessions.TimeProvider
22
24
import java.io.IOException
25
+ import java.util.concurrent.atomic.AtomicReference
23
26
import javax.inject.Inject
24
27
import javax.inject.Singleton
28
+ import kotlin.coroutines.CoroutineContext
29
+ import kotlinx.coroutines.CoroutineScope
25
30
import kotlinx.coroutines.flow.first
31
+ import kotlinx.coroutines.launch
26
32
import kotlinx.coroutines.runBlocking
27
33
28
34
internal interface SettingsCache {
@@ -41,23 +47,38 @@ internal interface SettingsCache {
41
47
internal class SettingsCacheImpl
42
48
@Inject
43
49
constructor (
50
+ @Background private val backgroundDispatcher: CoroutineContext ,
44
51
private val timeProvider: TimeProvider ,
45
52
private val sessionConfigsDataStore: DataStore <SessionConfigs >,
46
53
) : SettingsCache {
47
- private var sessionConfigs: SessionConfigs
54
+ private val sessionConfigsAtomicReference = AtomicReference <SessionConfigs >()
55
+
56
+ private val sessionConfigs: SessionConfigs
57
+ get() {
58
+ // Ensure configs are loaded from disk before the first access
59
+ if (sessionConfigsAtomicReference.get() == null ) {
60
+ // Double check to avoid the `runBlocking` unless necessary
61
+ sessionConfigsAtomicReference.compareAndSet(
62
+ null ,
63
+ runBlocking { sessionConfigsDataStore.data.first() },
64
+ )
65
+ }
66
+
67
+ return sessionConfigsAtomicReference.get()
68
+ }
48
69
49
70
init {
50
- // Block until the cache is loaded from disk to ensure cache
51
- // values are valid and readable from the main thread on init.
52
- runBlocking { sessionConfigs = sessionConfigsDataStore.data.first() }
71
+ CoroutineScope (backgroundDispatcher).launch {
72
+ sessionConfigsDataStore.data.collect(sessionConfigsAtomicReference::set)
73
+ }
53
74
}
54
75
55
76
override fun hasCacheExpired (): Boolean {
56
- val cacheUpdatedTimeMs = sessionConfigs.cacheUpdatedTimeMs
77
+ val cacheUpdatedTimeSeconds = sessionConfigs.cacheUpdatedTimeSeconds
57
78
val cacheDurationSeconds = sessionConfigs.cacheDurationSeconds
58
79
59
- if (cacheUpdatedTimeMs != null && cacheDurationSeconds != null ) {
60
- val timeDifferenceSeconds = ( timeProvider.currentTime().ms - cacheUpdatedTimeMs) / 1000
80
+ if (cacheUpdatedTimeSeconds != null && cacheDurationSeconds != null ) {
81
+ val timeDifferenceSeconds = timeProvider.currentTime().seconds - cacheUpdatedTimeSeconds
61
82
if (timeDifferenceSeconds < cacheDurationSeconds) {
62
83
return false
63
84
}
@@ -74,12 +95,12 @@ constructor(
74
95
override suspend fun updateConfigs (sessionConfigs : SessionConfigs ) {
75
96
try {
76
97
sessionConfigsDataStore.updateData { sessionConfigs }
77
- this .sessionConfigs = sessionConfigs
78
98
} catch (ex: IOException ) {
79
99
Log .w(TAG , " Failed to update config values: $ex " )
80
100
}
81
101
}
82
102
103
+ @VisibleForTesting
83
104
internal suspend fun removeConfigs () =
84
105
try {
85
106
sessionConfigsDataStore.updateData { SessionConfigsSerializer .defaultValue }
0 commit comments