Skip to content

Commit a411a64

Browse files
authored
Avoid IPC calls on main during map creation. (#2961)
1 parent 60eee54 commit a411a64

File tree

6 files changed

+82
-20
lines changed

6 files changed

+82
-20
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
Mapbox welcomes participation and contributions from everyone.
44

55
# main
6-
6+
## Features ✨ and improvements 🏁
7+
* Small performance improvements in the map initialization call chain by avoiding IPC calls on main.
78

89
# 11.10.0 February 13, 2025
910
## Features ✨ and improvements 🏁

maps-sdk/src/main/java/com/mapbox/maps/MapProvider.kt

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package com.mapbox.maps
22

33
import android.content.Context
4-
import android.os.Handler
5-
import android.os.Looper
64
import com.mapbox.annotation.module.MapboxModuleType
75
import com.mapbox.common.EventsServerOptions
86
import com.mapbox.common.EventsService
@@ -15,11 +13,23 @@ import com.mapbox.maps.geofencing.MapGeofencingConsent
1513
import com.mapbox.maps.module.MapTelemetry
1614
import com.mapbox.maps.plugin.MapDelegateProviderImpl
1715
import com.mapbox.maps.plugin.MapPluginRegistry
16+
import kotlinx.coroutines.CoroutineName
17+
import kotlinx.coroutines.CoroutineScope
18+
import kotlinx.coroutines.Dispatchers
19+
import kotlinx.coroutines.MainCoroutineDispatcher
20+
import kotlinx.coroutines.SupervisorJob
21+
import kotlinx.coroutines.launch
1822

1923
internal object MapProvider {
2024

2125
private lateinit var mapTelemetry: MapTelemetry
2226

27+
/**
28+
* [CoroutineScope] that runs on Main dispatcher by default (NOTE that it is not using [MainCoroutineDispatcher.immediate]).
29+
*/
30+
private val mainScope =
31+
CoroutineScope(CoroutineName(MapController.TAG) + SupervisorJob() + Dispatchers.Main)
32+
2333
fun getNativeMapWrapper(
2434
mapInitOptions: MapInitOptions,
2535
mapClient: MapClient,
@@ -54,7 +64,8 @@ internal object MapProvider {
5464
paramsProvider(context, MapboxModuleType.MapTelemetry)
5565
}
5666
}
57-
Handler(Looper.getMainLooper()).post {
67+
// Schedule the turnstile event on the Main dispatcher to avoid blocking this call chain.
68+
mainScope.launch {
5869
mapTelemetry.onAppUserTurnstileEvent()
5970
}
6071
return mapTelemetry

module-telemetry/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ mapboxLibrary {
2626
dependencies {
2727
implementation(project(":sdk-base"))
2828
implementation(libs.mapbox.base)
29+
implementation(libs.coroutines)
2930
compileOnly(libs.mapbox.annotations)
3031
kapt(libs.mapbox.annotationsProcessor)
3132
implementation(libs.kotlin)

module-telemetry/src/androidTest/java/com/mapbox/maps/module/telemetry/MapTelemetryEventsServiceTest.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import com.mapbox.common.EventsServiceObserver
1717
import com.mapbox.common.SdkInformation
1818
import com.mapbox.common.TelemetryService
1919
import com.mapbox.common.TelemetryUtils
20+
import kotlinx.coroutines.Dispatchers
2021
import org.junit.Assert
2122
import org.junit.Before
2223
import org.junit.BeforeClass
@@ -39,7 +40,7 @@ class MapTelemetryEventsServiceTest {
3940

4041
val telemetryService = TelemetryService.getOrCreate()
4142
val context = InstrumentationRegistry.getInstrumentation().context
42-
telemetry = MapTelemetryImpl(context, eventsService, telemetryService, options)
43+
telemetry = MapTelemetryImpl(context, eventsService, telemetryService, options, Dispatchers.Unconfined)
4344
}
4445

4546
@Test

module-telemetry/src/main/java/com/mapbox/maps/module/telemetry/MapTelemetryImpl.kt

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,28 @@ import com.google.gson.Gson
88
import com.mapbox.annotation.module.MapboxModule
99
import com.mapbox.annotation.module.MapboxModuleType
1010
import com.mapbox.bindgen.Value
11-
import com.mapbox.common.*
11+
import com.mapbox.common.Event
12+
import com.mapbox.common.EventPriority
13+
import com.mapbox.common.EventsServerOptions
14+
import com.mapbox.common.EventsService
15+
import com.mapbox.common.EventsServiceInterface
16+
import com.mapbox.common.SdkInformation
17+
import com.mapbox.common.TelemetryCollectionState
18+
import com.mapbox.common.TelemetryService
19+
import com.mapbox.common.TelemetryUtils
20+
import com.mapbox.common.TurnstileEvent
21+
import com.mapbox.common.UserSKUIdentifier
1222
import com.mapbox.maps.base.BuildConfig
1323
import com.mapbox.maps.logE
24+
import com.mapbox.maps.logW
1425
import com.mapbox.maps.module.MapTelemetry
15-
import java.util.*
26+
import kotlinx.coroutines.CoroutineDispatcher
27+
import kotlinx.coroutines.CoroutineName
28+
import kotlinx.coroutines.CoroutineScope
29+
import kotlinx.coroutines.Dispatchers
30+
import kotlinx.coroutines.SupervisorJob
31+
import kotlinx.coroutines.launch
32+
import java.util.UUID
1633

1734
/**
1835
* Concrete implementation of map telemetry.
@@ -27,7 +44,12 @@ class MapTelemetryImpl : MapTelemetry {
2744
private val eventsServiceOptions: EventsServerOptions
2845

2946
/**
30-
* Creates a map telemetry instance using application context and access token
47+
* [CoroutineScope] that runs on IO dispatcher by default.
48+
*/
49+
private val bgScope: CoroutineScope
50+
51+
/**
52+
* Creates a map telemetry instance using application context
3153
*
3254
* @param appContext the application context
3355
*/
@@ -44,6 +66,7 @@ class MapTelemetryImpl : MapTelemetry {
4466
)
4567
this.eventsService = EventsService.getOrCreate(eventsServiceOptions)
4668
this.telemetryService = TelemetryService.getOrCreate()
69+
bgScope = CoroutineScope(CoroutineName(TAG) + SupervisorJob() + Dispatchers.IO)
4770
}
4871

4972
/**
@@ -58,12 +81,14 @@ class MapTelemetryImpl : MapTelemetry {
5881
appContext: Context,
5982
eventsService: EventsServiceInterface,
6083
telemetryService: TelemetryService,
61-
eventsServerOptions: EventsServerOptions
84+
eventsServerOptions: EventsServerOptions,
85+
defaultCoroutineDispatcher: CoroutineDispatcher,
6286
) {
6387
this.appContext = appContext
6488
this.eventsService = eventsService
6589
this.telemetryService = telemetryService
6690
this.eventsServiceOptions = eventsServerOptions
91+
bgScope = CoroutineScope(CoroutineName(TAG) + SupervisorJob() + defaultCoroutineDispatcher)
6792
}
6893

6994
/**
@@ -77,8 +102,18 @@ class MapTelemetryImpl : MapTelemetry {
77102
}
78103

79104
if (shouldSendEvents()) {
80-
val mapLoadEvent = MapEventFactory.buildMapLoadEvent(PhoneState(appContext))
81-
sendEvent(Gson().toJson(mapLoadEvent))
105+
sendMapLoadEvent()
106+
}
107+
}
108+
109+
private fun sendMapLoadEvent() {
110+
bgScope.launch {
111+
try {
112+
val mapLoadEvent = MapEventFactory.buildMapLoadEvent(PhoneState(appContext))
113+
sendEvent(Gson().toJson(mapLoadEvent))
114+
} catch (e: Throwable) {
115+
logW(TAG, "sendMapLoadEvent error: $e")
116+
}
82117
}
83118
}
84119

module-telemetry/src/test/java/com/mapbox/maps/module/telemetry/MapTelemetryTest.kt

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import android.telephony.TelephonyManager
55
import android.view.Display
66
import android.view.WindowManager
77
import com.mapbox.common.*
8+
import com.mapbox.maps.base.BuildConfig
89
import io.mockk.*
10+
import kotlinx.coroutines.Dispatchers
911
import org.junit.After
1012
import org.junit.Assert.*
1113
import org.junit.Before
@@ -68,43 +70,54 @@ class MapTelemetryTest {
6870

6971
@Before
7072
fun setUp() {
71-
mockkStatic(EventsService::class)
72-
mockkStatic(TelemetryService::class)
7373
mockkStatic(TelemetryUtils::class)
7474
every { TelemetryUtils.getEventsCollectionState() } returns true
7575
every { TelemetryUtils.setEventsCollectionState(any(), any()) } returns Unit
7676
every { TelemetryUtils.getClientServerEventsCollectionState() } returns TelemetryCollectionState.ENABLED
7777

78-
every { EventsService.getOrCreate(any()) } returns eventsService
7978
every { eventsService.sendEvent(any(), any()) } returns Unit
8079
every { eventsService.sendTurnstileEvent(any(), any()) } returns Unit
8180

82-
every { TelemetryService.getOrCreate() } returns telemetryService
83-
8481
every { context.getSystemService(Context.TELEPHONY_SERVICE) } returns telephonyManager
8582
every { telephonyManager.networkType } returns TelephonyManager.NETWORK_TYPE_GPRS
8683
every { telephonyManager.networkOperatorName } returns "foobar"
8784

8885
every { context.getSystemService(Context.WINDOW_SERVICE) } returns windowManager
8986
every { windowManager.defaultDisplay } returns display
9087

91-
telemetry = MapTelemetryImpl(context)
88+
telemetry = MapTelemetryImpl(
89+
context, eventsService, telemetryService,
90+
EventsServerOptions(
91+
SdkInformation(
92+
BuildConfig.MAPBOX_SDK_IDENTIFIER,
93+
BuildConfig.MAPBOX_SDK_VERSION,
94+
null
95+
),
96+
null
97+
),
98+
Dispatchers.Unconfined
99+
)
92100
}
93101

94102
@After
95103
fun cleanUp() {
96-
unmockkStatic(EventsService::class)
97-
unmockkStatic(TelemetryService::class)
98104
unmockkStatic(TelemetryUtils::class)
99105
unmockkAll()
100106
}
101107

102108
@Test
103-
fun testConstructorInitialisation() {
109+
fun testDefaultConstructorInitialisation() {
110+
mockkStatic(EventsService::class)
111+
mockkStatic(TelemetryService::class)
112+
every { EventsService.getOrCreate(any()) } returns eventsService
113+
every { TelemetryService.getOrCreate() } returns telemetryService
114+
MapTelemetryImpl(context)
104115
// validate the event service is initialised
105116
verify { EventsService.getOrCreate(any()) }
106117
// validate the telemetry service is initialised
107118
verify { TelemetryService.getOrCreate() }
119+
unmockkStatic(EventsService::class)
120+
unmockkStatic(TelemetryService::class)
108121
}
109122

110123
@Test

0 commit comments

Comments
 (0)