MapLibre Android(Kotlin)加载并渲染MBTiles离线地图的实现方案咨询
MapLibre Android(Kotlin)加载并渲染MBTiles离线地图的实现方案咨询
我来帮你梳理下MapLibre Android加载MBTiles的正确姿势——刚好我之前做过类似的离线地图需求,给你一步步拆解问题和实现代码:
一、核心结论:MapLibre Android 完全支持MBTiles
但不能直接通过file://路径作为Source的URL传入!因为MBTiles是封装在SQLite数据库里的瓦片集合,不是直接的瓦片文件目录,必须用MapLibre官方提供的TileStore组件来加载,而不是直接读文件路径。
二、具体实现代码(Kotlin)
先讲前置准备,再分「栅格MBTiles」和「矢量MBTiles」两种情况给出可运行代码:
前置准备:处理MBTiles文件的存储
如果你把MBTiles放在assets目录,必须在build.gradle里配置禁止压缩该文件(SQLite文件压缩后会损坏):
// Groovy 版 build.gradle android { aaptOptions { noCompress "mbtiles" } } // Kotlin 版 build.gradle.kts android { aaptOptions { noCompress("mbtiles") } }
另外,assets里的文件是只读的,MapLibre的TileStore需要可访问的文件路径,推荐先把assets里的MBTiles复制到App私有目录(filesDir):
private fun copyMbtilesToPrivateDir(context: Context, assetFileName: String): File { val destFile = File(context.filesDir, assetFileName) if (destFile.exists()) return destFile // 从Assets复制到私有目录 context.assets.open(assetFileName).use { inputStream -> FileOutputStream(destFile).use { outputStream -> inputStream.copyTo(outputStream) } } return destFile }
情况1:加载栅格(Raster)MBTiles
// 在Activity/Fragment的onCreate里执行 val mapView = findViewById<MapView>(R.id.mapView) mapView.onCreate(savedInstanceState) // 1. 复制MBTiles到私有目录 val mbtilesFile = copyMbtilesToPrivateDir(this, "map.mbtiles") // 2. 初始化TileStore(绑定MBTiles文件) val tileStore = TileStore.create( this, TileStoreOptions.Builder() .path(mbtilesFile.absolutePath) .build() ) // 3. 加载空白Style,添加离线栅格源和图层 mapView.getMapboxMap().loadStyle(Style.EMPTY) { style -> // 创建绑定TileStore的栅格源 val rasterSource = RasterSource.Builder("offline-raster", TileStoreSourceOptions.Builder() .tileStore(tileStore) .tileStoreId("my-offline-raster") // 自定义瓦片集合ID,可任意命名 .build() ) .tileSize(256) // 要和你的MBTiles元数据里的tile_size一致 .build() style.addSource(rasterSource) // 添加栅格渲染图层 style.addLayer( RasterLayer.Builder("offline-raster-layer", "offline-raster") .build() ) // 可选:设置地图初始视角(用MBTiles元数据里的bounds) // mapView.getMapboxMap().setCamera(CameraOptions.Builder() // .center(LatLng(中心纬度, 中心经度)) // .zoom(10.0) // .build()) }
情况2:加载矢量(Vector)MBTiles
矢量MBTiles的核心流程和栅格一致,只是源和图层类型不同,且需要指定MBTiles内部的矢量图层名称(可以用MBTiles Viewer工具查看内部图层):
// 前面的复制文件、TileStore初始化和栅格版完全一致 mapView.getMapboxMap().loadStyle(Style.EMPTY) { style -> // 创建绑定TileStore的矢量源 val vectorSource = VectorSource.Builder("offline-vector", TileStoreSourceOptions.Builder() .tileStore(tileStore) .tileStoreId("my-offline-vector") .build() ) .build() style.addSource(vectorSource) // 添加矢量图层(示例:渲染道路图层,替换为你MBTiles里的实际图层名) style.addLayer( LineLayer.Builder("roads-layer", "offline-vector") .sourceLayer("roads") // 必须和MBTiles内部的图层名一致 .lineColor(Color.parseColor("#ff0000")) .lineWidth(2f) .build() ) // 同理,可添加其他矢量图层(比如土地、水域等) style.addLayer( FillLayer.Builder("land-layer", "offline-vector") .sourceLayer("land") .fillColor(Color.parseColor("#f0f0f0")) .build() ) }
三、关键注意事项
- MapLibre版本:必须用11.0.0+的版本,
TileStore的API在11.x才稳定,低版本可能有兼容性问题。 - MBTiles元数据校验:确保你的MBTiles文件是合法的——用MBTiles Viewer工具检查
metadata表是否有bounds、minzoom、maxzoom、format等关键字段,格式错误会导致加载失败。 - 权限问题:如果把MBTiles放在外部存储,Android 10+需要用Scoped Storage,推荐直接复制到App私有目录避免权限申请。
四、你的问题逐一解答
- Does MapLibre Android support loading MBTiles directly?
→ 是的,但必须通过TileStore组件,不能直接用file://路径加载。 - Is there a working Kotlin example?
→ 上面的代码就是可运行的完整示例,只需要替换你的MBTiles文件名和矢量图层名即可。 - If no, recommended approach?
→ 优先用官方的TileStore方案;如果有特殊需求,可将MBTiles转换为PMTiles(单文件瓦片格式,MapLibre 11.x+支持),转换工具用mbtiles-to-pmtiles;不推荐换SDK,MapLibre是目前开源离线地图的最优选择之一。
调试技巧
如果加载失败,去Logcat里搜索Tag为MapLibre的日志,里面会给出具体错误原因——比如MBTiles文件损坏、元数据缺失、路径错误等,方便快速定位问题。




