maplibre / maplibre-native

MapLibre Native - Interactive vector tile maps for iOS, Android and other platforms.
https://maplibre.org
BSD 2-Clause "Simplified" License
898 stars 274 forks source link

Out of Memory crash when downloading large bounds using OfflineManager on Android #2515

Open wissa-b opened 2 weeks ago

wissa-b commented 2 weeks ago

Describe the bug I'm trying to use Offline Manager to download parts of a vector map. Everything works fine when the map bounds I'm trying to download is not very large. When trying to download a large area for example a whole country, I get an info log scudo com.xyz.xyz ICan't populate more pages for size class 33296. And afterwards an out of memory crash happens and the app is closed. Here's the code I'm using for the offline manager:

suspend fun downloadBounds(
        context: Context,
        style: String,
        mapLatLngBounds: MapLatLngBounds,
    ): Result<Unit> {
        return suspendCancellableCoroutine { continuation ->
            val offlineManager = OfflineManager.getInstance(context)
            val regionDefinition = OfflineTilePyramidRegionDefinition(
                styleURL = style,
                bounds = mapLatLngBounds.toLatLngBounds(),
                minZoom = 10.0,
                maxZoom = 24.0,
                pixelRatio = 1.0f
            )
            val metadata = "{name: 'My Offline Region'}"
            val encodedMetadata = metadata.toByteArray()
            offlineManager.createOfflineRegion(
                definition = regionDefinition,
                metadata = encodedMetadata,
                object : OfflineManager.CreateOfflineRegionCallback {
                    override fun onCreate(offlineRegion: OfflineRegion) {
                        offlineRegion.setDownloadState(OfflineRegion.STATE_ACTIVE)
                        offlineRegion.setObserver(object : OfflineRegion.OfflineRegionObserver {
                            override fun mapboxTileCountLimitExceeded(limit: Long) {
                                continuation.resume(Result.failure(Throwable("mapboxTileCountLimitExceeded $limit")))
                            }

                            override fun onError(error: OfflineRegionError) {
                                if (continuation.isCompleted.not()) {
                                    continuation.resume(Result.failure(Throwable(error.message)))
                                } else {
                                    Timber.e("Continuation was completed already", error)
                                }
                            }

                            override fun onStatusChanged(status: OfflineRegionStatus) {
                                Timber.d(
                                    "onStatusChanged" +
                                        "downloadState= ${status.downloadState}," +
                                        "isComplete= ${status.isComplete}," +
                                        "completedTileCount= ${status.completedTileCount}"
                                )
                                if (status.isComplete) {
                                    if (continuation.isCompleted.not()) {
                                        continuation.resume(Result.success(Unit))
                                    } else {
                                        Timber.d("Continuation was completed already")
                                    }
                                }
                            }
                        })
                    }

                    override fun onError(error: String) {
                        continuation.resume(Result.failure(Throwable(message = error)))
                    }
                }
            )
        }
    }

An example bound that causes the crash are:

LatLngBounds.fromLatLngs(
    listOf(
LatLng(latitude=50.04072512059204, longitude=5.825740971157103),
LatLng(latitude=50.04072512059204, longitude=-10.314004461880756),
LatLng(latitude=24.314580424711707, longitude=5.825740971157103),
LatLng(latitude=24.314580424711707, longitude=-10.314004461880756)
)
)

Expected behavior

Platform information (please complete the following information):

Additional context Attached log file crash-log-android.txt

louwers commented 2 weeks ago

Downloading large areas with the offline manager is currently not supported well, because it downloads many individual tiles. Instead, you should prepare a single file on the server and let the user download this, then merge it in the offline database manually. We don't have good documentation how to do this currently.

A PR would be welcome to avoid the crash you are describing though.