Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions api/android-for-googlemaps.api
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,10 @@ public final class com.mapconductor.googlemaps.raster.GoogleMapRasterLayerOverla
}
final class com.mapconductor.googlemaps.raster.GoogleMapRasterLayerOverlayRenderer$Companion {
}
final class com.mapconductor.googlemaps.raster.GoogleMapRasterLayerOverlayRenderer$DetachableTileProvider implements com.google.android.gms.maps.model.TileProvider {
public com.google.android.gms.maps.model.Tile getTile(int, int, int);
public final void detach();
}
final class com.mapconductor.googlemaps.raster.GoogleMapRasterLayerOverlayRenderer$TileSpec {
public final java.lang.String getTemplate();
public final int getTileSize();
Expand Down
22 changes: 4 additions & 18 deletions src/main/java/com/mapconductor/googlemaps/GoogleMapView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ import com.mapconductor.googlemaps.polyline.GoogleMapPolylineOverlayRenderer
import com.mapconductor.googlemaps.raster.GoogleMapRasterLayerController
import com.mapconductor.googlemaps.raster.GoogleMapRasterLayerOverlayRenderer
import okhttp3.OkHttpClient
import android.view.ViewGroup
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.suspendCancellableCoroutine

Expand Down Expand Up @@ -142,7 +141,6 @@ fun GoogleMapView(
// Specific Google Maps DisposableEffect logic
val lifecycle = LocalLifecycleOwner.current.lifecycle // Get lifecycle here
DisposableEffect(lifecycle) {
val stateId = state.id
val observer =
object : DefaultLifecycleObserver {
override fun onResume(owner: LifecycleOwner) {
Expand All @@ -152,29 +150,17 @@ fun GoogleMapView(
override fun onPause(owner: LifecycleOwner) {
holderRef.value?.mapView?.onPause()
}

override fun onDestroy(owner: LifecycleOwner) {
val activity = context.findActivity()
if (activity?.isChangingConfigurations == true) {
holderRef.value?.mapView?.let {
(it.parent as? ViewGroup)?.removeView(it)
it.onDestroy()
}
} else {
// Real teardown (not a configuration change): free the
// controller's overlay controllers (tile-server routes,
// marker managers) and cancel its coroutine scope.
GoogleMapViewControllerStore.get(stateId)?.destroy()
GoogleMapViewControllerStore.remove(stateId)
}
}
}
lifecycle.addObserver(observer)
onDispose {
lifecycle.removeObserver(observer)
}
}
},
// The MapView keeps an SDK thread whose listeners reach the controller,
// so it has to be destroyed when the map leaves -- a provider switch as
// much as an Activity going away.
onDestroyMapView = { mapView -> mapView.onDestroy() },
sdkInitialize = {
sdkInitialize?.invoke(context) ?: true
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,13 @@ class GoogleMapViewState(
this.controller?.setMapDesignType(value)
}
get() = _mapDesignType
private var controller: GoogleMapViewControllerInterface? = null

// Read through the base so that letting go of the controller there (MapViewBase
// does it when the map is torn down) leaves nothing behind here.
private val controller: GoogleMapViewControllerInterface?
get() = attachedMapController as? GoogleMapViewControllerInterface

internal fun setController(controller: GoogleMapViewControllerInterface) {
this.controller = controller
// コントローラが覚えているデザインを state のものにしておく。既定の None のままだと、
// 変更リスナーを繋いだ時点で None が state へ返ってきて、復元したデザインを潰す。
// 地図は state の mapType で作られているので、地図の見た目は変わらない。
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ internal class GoogleMapRasterLayerController(
internal fun onCameraIdle() { tileRenderer.refreshLocalTilesAfterZoom() }

override fun destroy() {
tileRenderer.cancelLocalRequests()
tileRenderer.removeAllLayers()
super.destroy()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ class GoogleMapRasterLayerOverlayRenderer(
/** 差し替え済みで、新しい方のタイルが揃うのを待って外す古いオーバーレイ。 */
private val retiring = mutableSetOf<TileOverlay>()

/**
* 各オーバーレイに渡したタイルプロバイダ。SDK はオーバーレイを外しても、地図を
* 破棄しても、渡されたプロバイダをプロセス共有の表に残す(実測)。そこから
* このレンダラ・ホルダー・MapView まで辿れてしまうので、外すときに中身を切り離す。
*/
private val providers = mutableMapOf<TileOverlay, DetachableTileProvider>()

internal fun updateLocalZoom(zoom: Double) {
localRequests.values.forEach { it.updateZoom(zoom) }
}
Expand All @@ -62,6 +69,27 @@ class GoogleMapRasterLayerOverlayRenderer(
localRequests.clear()
}

/**
* Takes every overlay this renderer added off the map. Called when the map
* goes away: the SDK keeps a tile overlay's provider in a process-wide
* table until the overlay is removed, and the provider reaches this
* renderer and through it the map view. Must run on the main thread, before
* the MapView is destroyed.
*/
internal fun removeAllLayers() {
cancelLocalRequests()
(tileActivity.keys + retiring).forEach { overlay -> runCatching { overlay.remove() } }
tileActivity.clear()
retiring.clear()
providers.values.forEach { it.detach() }
providers.clear()
}

private fun forget(overlay: TileOverlay) {
tileActivity.remove(overlay)
providers.remove(overlay)?.detach()
}

private fun isMarkerTileRaster(state: RasterLayerState): Boolean = state.id.startsWith(MARKER_TILE_RASTER_ID_PREFIX)

private fun resolveOverlayZIndex(state: RasterLayerState): Float =
Expand Down Expand Up @@ -104,10 +132,13 @@ class GoogleMapRasterLayerOverlayRenderer(
withContext(coroutine.coroutineContext) {
data.forEach { entity ->
localRequests.remove(entity.layer)?.close()
tileActivity.remove(entity.layer)
forget(entity.layer)
entity.layer.remove()
}
retiring.forEach { it.remove() }
retiring.forEach {
forget(it)
it.remove()
}
retiring.clear()
}
}
Expand Down Expand Up @@ -136,7 +167,7 @@ class GoogleMapRasterLayerOverlayRenderer(
delay(RETIRE_POLL_MS)
}
if (retiring.remove(old)) {
tileActivity.remove(old)
forget(old)
old.remove()
}
}
Expand Down Expand Up @@ -256,14 +287,18 @@ class GoogleMapRasterLayerOverlayRenderer(
}
}
}
val detachable = DetachableTileProvider(provider)
val options =
TileOverlayOptions()
.tileProvider(provider)
.tileProvider(detachable)
.zIndex(resolveOverlayZIndex(state))
.transparency(opacityToTransparency(state.opacity))
.visible(state.visible)
val overlay = holder.map.addTileOverlay(options)
if (overlay != null) tileActivity[overlay] = activity
if (overlay != null) {
tileActivity[overlay] = activity
providers[overlay] = detachable
}
if (requests != null) {
if (overlay != null) localRequests[overlay] = requests else requests.close()
}
Expand Down Expand Up @@ -304,6 +339,21 @@ class GoogleMapRasterLayerOverlayRenderer(
private fun opacityToTransparency(opacity: Float): Float =
(1.0f - opacity.coerceIn(0.0f, 1.0f)).coerceIn(0.0f, 1.0f)

/** SDK に渡すのはこれ。[detach] したあとは何も返さず、元のプロバイダも掴まない。 */
private class DetachableTileProvider(
@Volatile private var delegate: TileProvider?,
) : TileProvider {
override fun getTile(
x: Int,
y: Int,
zoom: Int,
): Tile? = delegate?.getTile(x, y, zoom) ?: NO_TILE

fun detach() {
delegate = null
}
}

private data class TileSpec(
val template: String,
val tileSize: Int,
Expand Down
Loading