MapLibre Native Android
MapLibre Native renders the full vector outdoor style natively on Android with OpenGL ES, including 3D terrain.
Full working example: github.com/mapriot/map-examples/MaplibreNativeAndroidKotlin — complete Android Studio project with OkHttp API key interceptor.
Installation
Add to build.gradle (app module):
dependencies {
implementation 'org.maplibre.gl:android-sdk:11.+'
implementation 'com.squareup.okhttp3:okhttp:4.+'
}Add internet permission to AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />Basic activity (Kotlin)
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.Response
import org.maplibre.android.MapLibre
import org.maplibre.android.camera.CameraPosition
import org.maplibre.android.geometry.LatLng
import org.maplibre.android.maps.MapView
import org.maplibre.android.module.http.HttpRequestUtil
class MapActivity : AppCompatActivity() {
private lateinit var mapView: MapView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val apiKey = "YOUR_API_KEY"
MapLibre.getInstance(this)
// Build an OkHttpClient that appends ?apiKey= to all api.mapriot.com requests.
// This is the Android equivalent of transformRequest in MapLibre GL JS.
val client = OkHttpClient.Builder()
.addInterceptor(object : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val req = chain.request()
val url = req.url
if (url.host == "api.mapriot.com" && url.queryParameter("apiKey") == null) {
val newUrl = url.newBuilder()
.addQueryParameter("apiKey", apiKey)
.build()
return chain.proceed(req.newBuilder().url(newUrl).build())
}
return chain.proceed(req)
}
})
.build()
HttpRequestUtil.setOkHttpClient(client)
setContentView(R.layout.activity_main)
mapView = findViewById(R.id.mapView)
mapView.onCreate(savedInstanceState)
mapView.getMapAsync { map ->
map.setStyle("https://api.mapriot.com/styles/outdoor.json?apiKey=$apiKey")
map.cameraPosition = CameraPosition.Builder()
.target(LatLng(50.08, 14.42))
.zoom(13.0)
.build()
}
}
override fun onStart() { super.onStart(); mapView.onStart() }
override fun onResume() { super.onResume(); mapView.onResume() }
override fun onPause() { mapView.onPause(); super.onPause() }
override fun onStop() { mapView.onStop(); super.onStop() }
override fun onDestroy() { mapView.onDestroy(); super.onDestroy() }
override fun onLowMemory() { super.onLowMemory(); mapView.onLowMemory() }
override fun onSaveInstanceState(out: Bundle) { super.onSaveInstanceState(out); mapView.onSaveInstanceState(out) }
}Layout XML
<org.maplibre.android.maps.MapView
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent" />API key injection
The OkHttpClient interceptor above appends ?apiKey= to every request to api.mapriot.com. This ensures tile, font, and sprite subrequests are authenticated — the Android equivalent of transformRequest in MapLibre GL JS.
For a production app, store the API key securely (e.g. in local.properties exposed via BuildConfig) rather than hardcoding it. See the map-examples Android project for the full setup.
Notes
- Forward all lifecycle events to
mapView(onStart,onResume,onPause,onStop,onDestroy,onSaveInstanceState,onLowMemory) - The OkHttp interceptor is required — without it, only the style JSON loads and all tile requests fail
- The outdoor style URL with your API key loads the style; the interceptor handles everything else