Skip to content

Commit 06d200a

Browse files
committed
refactor(maps-compose): Make GoogleMapsInitializer fully suspending
The `initialize` function has been refactored to be a fully suspending operation. Key changes: - Replaced `launch(Dispatchers.IO)` with `withContext(Dispatchers.IO)`. This ensures the `initialize` function suspends until the blocking initialization call is complete, rather than returning immediately. - Added explicit handling for non-SUCCESS results from `MapsInitializer.initialize()` to set the state to `FAILURE`. - Restructured the `try-catch` block to correctly handle exceptions from the `withContext` block.
1 parent 6aefeda commit 06d200a

1 file changed

Lines changed: 33 additions & 18 deletions

File tree

maps-compose/src/main/java/com/google/maps/android/compose/internal/GoogleMapsInitializer.kt

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,7 @@ import com.google.android.gms.common.ConnectionResult
2121
import com.google.android.gms.maps.MapsInitializer
2222
import com.google.android.gms.maps.MapsApiSettings
2323
import com.google.maps.android.compose.meta.AttributionId
24-
import kotlinx.coroutines.CoroutineScope
2524
import kotlinx.coroutines.Dispatchers
26-
import kotlinx.coroutines.coroutineScope
27-
import kotlinx.coroutines.launch
2825
import kotlinx.coroutines.sync.Mutex
2926
import kotlinx.coroutines.sync.withLock
3027
import kotlinx.coroutines.withContext
@@ -94,29 +91,47 @@ public object GoogleMapsInitializer {
9491
* @param context The context to use for initialization.
9592
*/
9693
public suspend fun initialize(context: Context) {
94+
// 1. Quick exit if already initialized or in progress.
9795
if (_state.value != InitializationState.UNINITIALIZED) {
9896
return
9997
}
100-
coroutineScope {
101-
mutex.withLock {
102-
// Re-check state to prevent re-initialization even when calling this function in parallel
103-
if (_state.value != InitializationState.UNINITIALIZED) {
104-
return@withLock
105-
}
106-
_state.value = InitializationState.INITIALIZING
98+
99+
// 2. Acquire the mutex, perform a double-check (in case another
100+
// coroutine was also waiting), and set the state.
101+
// This block is synchronous and ensures only one coroutine
102+
// proceeds to the IO operation.
103+
mutex.withLock {
104+
if (_state.value != InitializationState.UNINITIALIZED) {
105+
return // Another coroutine won the race while this one was suspended on the lock.
107106
}
107+
_state.value = InitializationState.INITIALIZING
108+
}
109+
110+
// The lock is now released.
108111

109-
launch(Dispatchers.IO) {
110-
try {
111-
if (MapsInitializer.initialize(context) == ConnectionResult.SUCCESS) {
112-
MapsApiSettings.addInternalUsageAttributionId(context, attributionId)
113-
_state.value = InitializationState.SUCCESS
114-
}
115-
} catch (e: Exception) {
116-
// In tests where the map is mocked, this can fail.
112+
// 3. Run the blocking initialization code on the IO dispatcher.
113+
// This function will SUSPEND until the withContext(Dispatchers.IO) block completes.
114+
// If the calling scope is cancelled while waiting, withContext will throw
115+
// a CancellationException, and the state will remain INITIALIZING
116+
// (which the catch block will update to FAILURE).
117+
try {
118+
withContext(Dispatchers.IO) {
119+
// This is the blocking call. The thread will be blocked here.
120+
// If cancellation happens, the thread STILL finishes this call,
121+
// but the coroutine will immediately throw CancellationException
122+
// *after* this call returns, skipping the state assignments below.
123+
if (MapsInitializer.initialize(context) == ConnectionResult.SUCCESS) {
124+
MapsApiSettings.addInternalUsageAttributionId(context, attributionId)
125+
_state.value = InitializationState.SUCCESS
126+
} else {
127+
// Handle cases where initialize() returns a non-SUCCESS code
117128
_state.value = InitializationState.FAILURE
118129
}
119130
}
131+
} catch (_: Exception) {
132+
// This will catch any exceptions from the init process (like from mocks in tests)
133+
// Note: By default, this does NOT catch CancellationException.
134+
_state.value = InitializationState.FAILURE
120135
}
121136
}
122137

0 commit comments

Comments
 (0)