Skip to content

Commit ba97c68

Browse files
authored
Merge 306bdc6 into 62508f2
2 parents 62508f2 + 306bdc6 commit ba97c68

File tree

10 files changed

+365
-388
lines changed

10 files changed

+365
-388
lines changed

firebase-sessions/src/androidTest/kotlin/com/google/firebase/sessions/FirebaseSessionsTests.kt

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,10 @@ import androidx.test.core.app.ApplicationProvider
2020
import androidx.test.ext.junit.runners.AndroidJUnit4
2121
import com.google.common.truth.Truth.assertThat
2222
import com.google.firebase.Firebase
23-
import com.google.firebase.FirebaseApp
2423
import com.google.firebase.FirebaseOptions
2524
import com.google.firebase.initialize
2625
import com.google.firebase.sessions.settings.SessionsSettings
27-
import org.junit.After
28-
import org.junit.Before
26+
import org.junit.BeforeClass
2927
import org.junit.Test
3028
import org.junit.runner.RunWith
3129

@@ -36,23 +34,6 @@ import org.junit.runner.RunWith
3634
*/
3735
@RunWith(AndroidJUnit4::class)
3836
class FirebaseSessionsTests {
39-
@Before
40-
fun setUp() {
41-
Firebase.initialize(
42-
ApplicationProvider.getApplicationContext(),
43-
FirebaseOptions.Builder()
44-
.setApplicationId(APP_ID)
45-
.setApiKey(API_KEY)
46-
.setProjectId(PROJECT_ID)
47-
.build()
48-
)
49-
}
50-
51-
@After
52-
fun cleanUp() {
53-
FirebaseApp.clearInstancesForTest()
54-
}
55-
5637
@Test
5738
fun firebaseSessionsDoesInitialize() {
5839
assertThat(FirebaseSessions.instance).isNotNull()
@@ -69,5 +50,18 @@ class FirebaseSessionsTests {
6950
private const val APP_ID = "1:1:android:1a"
7051
private const val API_KEY = "API-KEY-API-KEY-API-KEY-API-KEY-API-KEY"
7152
private const val PROJECT_ID = "PROJECT-ID"
53+
54+
@BeforeClass
55+
@JvmStatic
56+
fun setUp() {
57+
Firebase.initialize(
58+
ApplicationProvider.getApplicationContext(),
59+
FirebaseOptions.Builder()
60+
.setApplicationId(APP_ID)
61+
.setApiKey(API_KEY)
62+
.setProjectId(PROJECT_ID)
63+
.build(),
64+
)
65+
}
7266
}
7367
}

firebase-sessions/src/main/kotlin/com/google/firebase/sessions/settings/RemoteSettings.kt

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,11 @@ import com.google.firebase.installations.FirebaseInstallationsApi
2323
import com.google.firebase.sessions.ApplicationInfo
2424
import com.google.firebase.sessions.InstallationId
2525
import com.google.firebase.sessions.TimeProvider
26-
import dagger.Lazy
2726
import javax.inject.Inject
2827
import javax.inject.Singleton
2928
import kotlin.time.Duration
3029
import kotlin.time.Duration.Companion.hours
3130
import kotlin.time.Duration.Companion.seconds
32-
import kotlinx.coroutines.runBlocking
3331
import kotlinx.coroutines.sync.Mutex
3432
import kotlinx.coroutines.sync.withLock
3533
import org.json.JSONException
@@ -43,11 +41,8 @@ constructor(
4341
private val firebaseInstallationsApi: FirebaseInstallationsApi,
4442
private val appInfo: ApplicationInfo,
4543
private val configsFetcher: CrashlyticsSettingsFetcher,
46-
private val lazySettingsCache: Lazy<SettingsCache>,
44+
private val settingsCache: SettingsCache,
4745
) : SettingsProvider {
48-
private val settingsCache: SettingsCache
49-
get() = lazySettingsCache.get()
50-
5146
private val fetchInProgress = Mutex()
5247

5348
override val sessionEnabled: Boolean?
@@ -133,7 +128,7 @@ constructor(
133128
sessionTimeoutSeconds = sessionTimeoutSeconds,
134129
sessionSamplingRate = sessionSamplingRate,
135130
cacheDurationSeconds = cacheDuration ?: defaultCacheDuration,
136-
cacheUpdatedTimeMs = timeProvider.currentTime().ms,
131+
cacheUpdatedTimeSeconds = timeProvider.currentTime().seconds,
137132
)
138133
)
139134
},
@@ -148,7 +143,7 @@ constructor(
148143
override fun isSettingsStale(): Boolean = settingsCache.hasCacheExpired()
149144

150145
@VisibleForTesting
151-
internal fun clearCachedSettings() = runBlocking {
146+
internal suspend fun clearCachedSettings() {
152147
settingsCache.updateConfigs(SessionConfigsSerializer.defaultValue)
153148
}
154149

firebase-sessions/src/main/kotlin/com/google/firebase/sessions/settings/SessionConfigs.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ internal data class SessionConfigs(
3030
val sessionSamplingRate: Double?,
3131
val sessionTimeoutSeconds: Int?,
3232
val cacheDurationSeconds: Int?,
33-
val cacheUpdatedTimeMs: Long?,
33+
val cacheUpdatedTimeSeconds: Long?,
3434
)
3535

3636
/** DataStore json [Serializer] for [SessionConfigs]. */
@@ -41,7 +41,7 @@ internal object SessionConfigsSerializer : Serializer<SessionConfigs> {
4141
sessionSamplingRate = null,
4242
sessionTimeoutSeconds = null,
4343
cacheDurationSeconds = null,
44-
cacheUpdatedTimeMs = null,
44+
cacheUpdatedTimeSeconds = null,
4545
)
4646

4747
override suspend fun readFrom(input: InputStream): SessionConfigs =

firebase-sessions/src/main/kotlin/com/google/firebase/sessions/settings/SettingsCache.kt

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,18 @@
1717
package com.google.firebase.sessions.settings
1818

1919
import android.util.Log
20+
import androidx.annotation.VisibleForTesting
2021
import androidx.datastore.core.DataStore
22+
import com.google.firebase.annotations.concurrent.Background
2123
import com.google.firebase.sessions.TimeProvider
2224
import java.io.IOException
25+
import java.util.concurrent.atomic.AtomicReference
2326
import javax.inject.Inject
2427
import javax.inject.Singleton
28+
import kotlin.coroutines.CoroutineContext
29+
import kotlinx.coroutines.CoroutineScope
2530
import kotlinx.coroutines.flow.first
31+
import kotlinx.coroutines.launch
2632
import kotlinx.coroutines.runBlocking
2733

2834
internal interface SettingsCache {
@@ -41,23 +47,38 @@ internal interface SettingsCache {
4147
internal class SettingsCacheImpl
4248
@Inject
4349
constructor(
50+
@Background private val backgroundDispatcher: CoroutineContext,
4451
private val timeProvider: TimeProvider,
4552
private val sessionConfigsDataStore: DataStore<SessionConfigs>,
4653
) : 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+
}
4869

4970
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+
}
5374
}
5475

5576
override fun hasCacheExpired(): Boolean {
56-
val cacheUpdatedTimeMs = sessionConfigs.cacheUpdatedTimeMs
77+
val cacheUpdatedTimeSeconds = sessionConfigs.cacheUpdatedTimeSeconds
5778
val cacheDurationSeconds = sessionConfigs.cacheDurationSeconds
5879

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
6182
if (timeDifferenceSeconds < cacheDurationSeconds) {
6283
return false
6384
}
@@ -74,12 +95,12 @@ constructor(
7495
override suspend fun updateConfigs(sessionConfigs: SessionConfigs) {
7596
try {
7697
sessionConfigsDataStore.updateData { sessionConfigs }
77-
this.sessionConfigs = sessionConfigs
7898
} catch (ex: IOException) {
7999
Log.w(TAG, "Failed to update config values: $ex")
80100
}
81101
}
82102

103+
@VisibleForTesting
83104
internal suspend fun removeConfigs() =
84105
try {
85106
sessionConfigsDataStore.updateData { SessionConfigsSerializer.defaultValue }

firebase-sessions/src/test/kotlin/com/google/firebase/sessions/SessionDatastoreTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ class SessionDatastoreTest {
4444
DataStoreFactory.create(
4545
serializer = SessionDataSerializer,
4646
scope = CoroutineScope(StandardTestDispatcher(testScheduler, "blocking")),
47-
produceFile = { appContext.dataStoreFile("sessionDataTestDataStore.data") },
47+
produceFile = { appContext.dataStoreFile("sessionDataStore.data") },
4848
),
4949
)
5050

0 commit comments

Comments
 (0)