Skip to content

Commit b0fe69f

Browse files
stanis-kjush
authored andcommitted
Drop the renderManeuverUpdate if its value has not changed. (#9970)
* Drop the renderManeuverUpdate if its value has not changed. * Update projects/mapbox-navigation-android-internal/mapbox-navigation-android/ui-maps/src/main/java/com/mapbox/navigation/ui/maps/route/arrow/api/MapboxRouteArrowView.kt Co-authored-by: Ramon <[email protected]> * Refactor MapboxRouteArrowView render funcs for consistency * Add changelog * Fix hashing function --------- Co-authored-by: Ramon <[email protected]> GitOrigin-RevId: ee6f185e2466fd23009c0a1aa80daa21c2a34a66
1 parent 65c944b commit b0fe69f

File tree

9 files changed

+248
-72
lines changed

9 files changed

+248
-72
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Optimize the `MapboxRouteArrowView` to skip re-rendering arrows that have not changed.

ui-maps/src/main/java/com/mapbox/navigation/ui/maps/route/arrow/api/MapboxRouteArrowView.kt

Lines changed: 76 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import com.mapbox.navigation.ui.maps.route.arrow.model.RouteArrowOptions
2424
import com.mapbox.navigation.ui.maps.route.arrow.model.UpdateManeuverArrowValue
2525
import com.mapbox.navigation.ui.maps.util.sdkStyleManager
2626
import com.mapbox.navigation.utils.internal.logE
27+
import java.util.Objects
2728

2829
/**
2930
* Responsible for rendering state data generated by the MapboxRouteArrowApi class. The
@@ -54,19 +55,29 @@ class MapboxRouteArrowView(private val options: RouteArrowOptions) {
5455
*/
5556
private val currentSourceHashes = mutableMapOf<String, Int>()
5657

58+
/**
59+
* The last [renderInternal] hash. Used to determine if the maneuver arrow data has changed.
60+
* If the data has not changed, the sources and layers will not be rebuilt.
61+
* This is used to avoid unnecessary calls to [rebuildSourcesAndLayersIfNeeded] and
62+
* [updateLayerVisibility] methods, which can be expensive operations due to JNI calls.
63+
*/
64+
private var lastRenderHash: Int? = null
65+
5766
/**
5867
* Renders an [ArrowVisibilityChangeValue] applying view side effects based on the data
5968
* it contains.
6069
*
6170
* @param style a valid map style object
6271
* @param visibilityChange a state containing data for applying the view side effects.
6372
*/
64-
fun render(style: Style, visibilityChange: ArrowVisibilityChangeValue) {
65-
rebuildSourcesAndLayersIfNeeded(style)
66-
67-
visibilityChange.layerVisibilityModifications.forEach {
68-
updateLayerVisibility(style, it.first, it.second)
69-
}
73+
fun render(
74+
style: Style,
75+
visibilityChange: ArrowVisibilityChangeValue,
76+
) {
77+
renderInternal(
78+
style = style,
79+
arrowVisibiliyChange = visibilityChange.layerVisibilityModifications,
80+
)
7081
}
7182

7283
/**
@@ -80,21 +91,18 @@ class MapboxRouteArrowView(private val options: RouteArrowOptions) {
8091
style: Style,
8192
expectedValue: Expected<InvalidPointError, UpdateManeuverArrowValue>,
8293
) {
83-
rebuildSourcesAndLayersIfNeeded(style)
84-
8594
expectedValue.onError {
8695
logE(it.errorMessage, LOG_CATEGORY)
8796
}
8897
expectedValue.onValue { value ->
89-
value.layerVisibilityModifications.forEach {
90-
updateLayerVisibility(style, it.first, it.second)
91-
}
92-
value.arrowHeadFeature?.apply {
93-
updateSource(style, RouteLayerConstants.ARROW_HEAD_SOURCE_ID, this)
94-
}
95-
value.arrowShaftFeature?.apply {
96-
updateSource(style, RouteLayerConstants.ARROW_SHAFT_SOURCE_ID, this)
97-
}
98+
renderInternal(
99+
style = style,
100+
arrowVisibiliyChange = value.layerVisibilityModifications,
101+
shaftFeatureCollection = value.arrowShaftFeature?.let(
102+
FeatureCollection::fromFeature,
103+
),
104+
headFeatureCollection = value.arrowHeadFeature?.let(FeatureCollection::fromFeature),
105+
)
98106
}
99107
}
100108

@@ -105,17 +113,10 @@ class MapboxRouteArrowView(private val options: RouteArrowOptions) {
105113
* @param arrowAdded a state containing data for applying the view side effects.
106114
*/
107115
fun render(style: Style, arrowAdded: ArrowAddedValue) {
108-
rebuildSourcesAndLayersIfNeeded(style)
109-
110-
updateSource(
111-
style,
112-
RouteLayerConstants.ARROW_SHAFT_SOURCE_ID,
113-
arrowAdded.arrowShaftFeatureCollection,
114-
)
115-
updateSource(
116-
style,
117-
RouteLayerConstants.ARROW_HEAD_SOURCE_ID,
118-
arrowAdded.arrowHeadFeatureCollection,
116+
renderInternal(
117+
style = style,
118+
shaftFeatureCollection = arrowAdded.arrowShaftFeatureCollection,
119+
headFeatureCollection = arrowAdded.arrowHeadFeatureCollection,
119120
)
120121
}
121122

@@ -143,17 +144,10 @@ class MapboxRouteArrowView(private val options: RouteArrowOptions) {
143144
* @param state a state containing data for applying the view side effects.
144145
*/
145146
fun render(style: Style, state: RemoveArrowValue) {
146-
rebuildSourcesAndLayersIfNeeded(style)
147-
148-
updateSource(
149-
style,
150-
RouteLayerConstants.ARROW_SHAFT_SOURCE_ID,
151-
state.arrowShaftFeatureCollection,
152-
)
153-
updateSource(
154-
style,
155-
RouteLayerConstants.ARROW_HEAD_SOURCE_ID,
156-
state.arrowHeadFeatureCollection,
147+
renderInternal(
148+
style = style,
149+
shaftFeatureCollection = state.arrowShaftFeatureCollection,
150+
headFeatureCollection = state.arrowHeadFeatureCollection,
157151
)
158152
}
159153

@@ -164,17 +158,10 @@ class MapboxRouteArrowView(private val options: RouteArrowOptions) {
164158
* @param state a state containing data for applying the view side effects.
165159
*/
166160
fun render(style: Style, state: ClearArrowsValue) {
167-
rebuildSourcesAndLayersIfNeeded(style)
168-
169-
updateSource(
170-
style,
171-
RouteLayerConstants.ARROW_SHAFT_SOURCE_ID,
172-
state.arrowShaftFeatureCollection,
173-
)
174-
updateSource(
175-
style,
176-
RouteLayerConstants.ARROW_HEAD_SOURCE_ID,
177-
state.arrowHeadFeatureCollection,
161+
renderInternal(
162+
style = style,
163+
shaftFeatureCollection = state.arrowShaftFeatureCollection,
164+
headFeatureCollection = state.arrowHeadFeatureCollection,
178165
)
179166
}
180167

@@ -192,22 +179,49 @@ class MapboxRouteArrowView(private val options: RouteArrowOptions) {
192179
)
193180
}
194181

195-
private fun updateLayerVisibility(style: Style, layerId: String, visibility: Visibility) {
196-
style.getLayer(layerId)?.apply {
197-
if (this.visibility != visibility) {
198-
this.visibility(visibility)
182+
private fun renderInternal(
183+
style: Style,
184+
arrowVisibiliyChange: List<Pair<String, Visibility>>? = null,
185+
shaftFeatureCollection: FeatureCollection? = null,
186+
headFeatureCollection: FeatureCollection? = null,
187+
) {
188+
val hash = Objects.hash(arrowVisibiliyChange, shaftFeatureCollection, headFeatureCollection)
189+
190+
if (hash == lastRenderHash) {
191+
return
192+
}
193+
194+
lastRenderHash = hash
195+
196+
rebuildSourcesAndLayersIfNeeded(style)
197+
198+
if (arrowVisibiliyChange != null) {
199+
arrowVisibiliyChange.forEach {
200+
updateLayerVisibility(style, it.first, it.second)
199201
}
200202
}
203+
204+
if (shaftFeatureCollection != null) {
205+
updateSource(
206+
style,
207+
RouteLayerConstants.ARROW_SHAFT_SOURCE_ID,
208+
shaftFeatureCollection,
209+
)
210+
}
211+
212+
if (headFeatureCollection != null) {
213+
updateSource(
214+
style,
215+
RouteLayerConstants.ARROW_HEAD_SOURCE_ID,
216+
headFeatureCollection,
217+
)
218+
}
201219
}
202220

203-
private fun updateSource(style: Style, sourceId: String, feature: Feature) {
204-
val newFeatureHash = feature.hashCode()
205-
// Only update the sources if it has changed
206-
if (currentSourceHashes[sourceId] != newFeatureHash) {
207-
val geoJsonSource = style.getSourceAs<GeoJsonSource>(sourceId)
208-
if (geoJsonSource != null) {
209-
geoJsonSource.feature(feature)
210-
currentSourceHashes[sourceId] = newFeatureHash
221+
private fun updateLayerVisibility(style: Style, layerId: String, visibility: Visibility) {
222+
style.getLayer(layerId)?.apply {
223+
if (this.visibility != visibility) {
224+
this.visibility(visibility)
211225
}
212226
}
213227
}

ui-maps/src/main/java/com/mapbox/navigation/ui/maps/route/arrow/model/ArrowAddedValue.kt

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,29 @@ import com.mapbox.geojson.FeatureCollection
1111
class ArrowAddedValue internal constructor(
1212
val arrowShaftFeatureCollection: FeatureCollection,
1313
val arrowHeadFeatureCollection: FeatureCollection,
14-
)
14+
) {
15+
override fun equals(other: Any?): Boolean {
16+
if (this === other) return true
17+
if (javaClass != other?.javaClass) return false
18+
19+
other as ArrowAddedValue
20+
21+
if (arrowShaftFeatureCollection != other.arrowShaftFeatureCollection) return false
22+
if (arrowHeadFeatureCollection != other.arrowHeadFeatureCollection) return false
23+
24+
return true
25+
}
26+
27+
override fun hashCode(): Int {
28+
var result = arrowShaftFeatureCollection.hashCode()
29+
result = 31 * result + arrowHeadFeatureCollection.hashCode()
30+
return result
31+
}
32+
33+
override fun toString(): String {
34+
return "ArrowAddedValue(" +
35+
"arrowShaftFeatureCollection=$arrowShaftFeatureCollection, " +
36+
"arrowHeadFeatureCollection=$arrowHeadFeatureCollection" +
37+
")"
38+
}
39+
}

ui-maps/src/main/java/com/mapbox/navigation/ui/maps/route/arrow/model/ArrowVisibilityChangeValue.kt

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,25 @@ import com.mapbox.maps.extension.style.layers.properties.generated.Visibility
99
*/
1010
class ArrowVisibilityChangeValue internal constructor(
1111
val layerVisibilityModifications: List<Pair<String, Visibility>>,
12-
)
12+
) {
13+
override fun equals(other: Any?): Boolean {
14+
if (this === other) return true
15+
if (javaClass != other?.javaClass) return false
16+
17+
other as ArrowVisibilityChangeValue
18+
19+
if (layerVisibilityModifications != other.layerVisibilityModifications) return false
20+
21+
return true
22+
}
23+
24+
override fun hashCode(): Int {
25+
return layerVisibilityModifications.hashCode()
26+
}
27+
28+
override fun toString(): String {
29+
return "ArrowVisibilityChangeValue(" +
30+
"layerVisibilityModifications=$layerVisibilityModifications" +
31+
")"
32+
}
33+
}

ui-maps/src/main/java/com/mapbox/navigation/ui/maps/route/arrow/model/ClearArrowsValue.kt

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,29 @@ import com.mapbox.geojson.FeatureCollection
1111
class ClearArrowsValue internal constructor(
1212
val arrowShaftFeatureCollection: FeatureCollection,
1313
val arrowHeadFeatureCollection: FeatureCollection,
14-
)
14+
) {
15+
override fun equals(other: Any?): Boolean {
16+
if (this === other) return true
17+
if (javaClass != other?.javaClass) return false
18+
19+
other as ClearArrowsValue
20+
21+
if (arrowShaftFeatureCollection != other.arrowShaftFeatureCollection) return false
22+
if (arrowHeadFeatureCollection != other.arrowHeadFeatureCollection) return false
23+
24+
return true
25+
}
26+
27+
override fun hashCode(): Int {
28+
var result = arrowShaftFeatureCollection.hashCode()
29+
result = 31 * result + arrowHeadFeatureCollection.hashCode()
30+
return result
31+
}
32+
33+
override fun toString(): String {
34+
return "ClearArrowsValue(" +
35+
"arrowShaftFeatureCollection=$arrowShaftFeatureCollection, " +
36+
"arrowHeadFeatureCollection=$arrowHeadFeatureCollection" +
37+
")"
38+
}
39+
}

ui-maps/src/main/java/com/mapbox/navigation/ui/maps/route/arrow/model/ManeuverArrow.kt

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,25 @@ import com.mapbox.geojson.Point
1010
*
1111
* @param points a collection of points defining the arrow
1212
*/
13-
class ManeuverArrow(val points: List<Point>)
13+
class ManeuverArrow(val points: List<Point>) {
14+
override fun equals(other: Any?): Boolean {
15+
if (this === other) return true
16+
if (javaClass != other?.javaClass) return false
17+
18+
other as ManeuverArrow
19+
20+
if (points != other.points) return false
21+
22+
return true
23+
}
24+
25+
override fun hashCode(): Int {
26+
return points.hashCode()
27+
}
28+
29+
override fun toString(): String {
30+
return "ManeuverArrow(" +
31+
"points=$points" +
32+
")"
33+
}
34+
}

ui-maps/src/main/java/com/mapbox/navigation/ui/maps/route/arrow/model/RemoveArrowValue.kt

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,29 @@ import com.mapbox.geojson.FeatureCollection
1111
class RemoveArrowValue internal constructor(
1212
val arrowShaftFeatureCollection: FeatureCollection,
1313
val arrowHeadFeatureCollection: FeatureCollection,
14-
)
14+
) {
15+
override fun equals(other: Any?): Boolean {
16+
if (this === other) return true
17+
if (javaClass != other?.javaClass) return false
18+
19+
other as RemoveArrowValue
20+
21+
if (arrowShaftFeatureCollection != other.arrowShaftFeatureCollection) return false
22+
if (arrowHeadFeatureCollection != other.arrowHeadFeatureCollection) return false
23+
24+
return true
25+
}
26+
27+
override fun hashCode(): Int {
28+
var result = arrowShaftFeatureCollection.hashCode()
29+
result = 31 * result + arrowHeadFeatureCollection.hashCode()
30+
return result
31+
}
32+
33+
override fun toString(): String {
34+
return "RemoveArrowValue(" +
35+
"arrowShaftFeatureCollection=$arrowShaftFeatureCollection, " +
36+
"arrowHeadFeatureCollection=$arrowHeadFeatureCollection" +
37+
")"
38+
}
39+
}

ui-maps/src/main/java/com/mapbox/navigation/ui/maps/route/arrow/model/UpdateManeuverArrowValue.kt

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,32 @@ class UpdateManeuverArrowValue internal constructor(
1414
val layerVisibilityModifications: List<Pair<String, Visibility>>,
1515
val arrowShaftFeature: Feature?,
1616
val arrowHeadFeature: Feature?,
17-
)
17+
) {
18+
override fun equals(other: Any?): Boolean {
19+
if (this === other) return true
20+
if (javaClass != other?.javaClass) return false
21+
22+
other as UpdateManeuverArrowValue
23+
24+
if (layerVisibilityModifications != other.layerVisibilityModifications) return false
25+
if (arrowShaftFeature != other.arrowShaftFeature) return false
26+
if (arrowHeadFeature != other.arrowHeadFeature) return false
27+
28+
return true
29+
}
30+
31+
override fun hashCode(): Int {
32+
var result = layerVisibilityModifications.hashCode()
33+
result = 31 * result + (arrowShaftFeature?.hashCode() ?: 0)
34+
result = 31 * result + (arrowHeadFeature?.hashCode() ?: 0)
35+
return result
36+
}
37+
38+
override fun toString(): String {
39+
return "UpdateManeuverArrowValue(" +
40+
"layerVisibilityModifications=$layerVisibilityModifications, " +
41+
"arrowShaftFeature=$arrowShaftFeature, " +
42+
"arrowHeadFeature=$arrowHeadFeature" +
43+
")"
44+
}
45+
}

0 commit comments

Comments
 (0)