本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。
在亚马逊Location Service 中使用安卓版 MapLibre GL Native SDK
使用 MapLibre GL Native
适用于 Android 的 MapLibre GL Native SDK 是一个基于 Mapbox GL Nat
本教程介绍如何将安卓版 MapLibre GL Native SDK 与亚马逊位置集成。本教程的示例应用程序作为亚马逊Location Service 示例存储库的一部分提供GitHub
构建应用程序:初始化
要初始化应用程序,请执行以下操作:
-
使用空白活动模板创建一个新的 Android Studio 项目。
-
确保选择 Kotlin 作为项目语言。
-
选择 API 14 的最低 SDK:安卓 4.0(Ice Cream Sandwich)或更高版本。
-
打开 “项目结构”,然后转到 “文件” > “项目结构...” 选择 “依赖关系” 部分。
-
<All Modules>选中后,选择 + 按钮添加新的库依赖关系。
-
添加 AWS 安卓软件开发工具包版本 2.20.0 或更高版本。例如:
com.amazonaws:aws-android-sdk-core:2.20.0
-
添加适用于安卓版本 9.4.0 或更高版本的MapLibre GL 原生 SDK。例如:
org.maplibre.gl:android-sdk:9.4.0
-
在 build.gradle 文件的项目级别,添加以下 maven 存储库以访问适用于 Android 的 MapLibre软件包:
allprojects { repositories { // Retain your existing repositories google() jcenter() // Declare the repositories for MapLibre mavenCentral() } }
构建应用程序:配置
要使用您的资源和AWS区域配置应用程序,请执行以下操作:
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="identityPoolId">
us-east-1:54f2ba88-9390-498d-aaa5-0d97fb7ca3bd
</string> <string name="mapName">ExampleMap
</string> <string name="awsRegion">us-east-1
</string> </resources>
构建应用程序:活动布局
编辑app/src/main/res/layout/activity_main.xml
:
-
添加 a
MapView
,用于渲染地图。这也将设置地图的初始中心点。 -
添加 a
TextView
,显示归因。
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <com.mapbox.mapboxsdk.maps.MapView android:id="@+id/mapView" android:layout_width="match_parent" android:layout_height="match_parent" app:mapbox_cameraTargetLat="49.2819" app:mapbox_cameraTargetLng="-123.1187" app:mapbox_cameraZoom="12" app:mapbox_uiAttribution="false" app:mapbox_uiLogo="false" /> <TextView android:id="@+id/attributionView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="#80808080" android:padding="5sp" android:textColor="@android:color/black" android:textSize="10sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" tools:ignore="SmallSp" /> </androidx.constraintlayout.widget.ConstraintLayout>
构建应用程序:请求转换
创建一个名SigV4Interceptor
为拦截 AWS 请求的类,并使用签名版本 4 对请求进行签名。将在创建主活动时将其注册到用于获取地图资源的 HTTP 客户端。
package aws.location.demo.okhttp import com.amazonaws.DefaultRequest import com.amazonaws.auth.AWS4Signer import com.amazonaws.auth.AWSCredentialsProvider import com.amazonaws.http.HttpMethodName import com.amazonaws.util.IOUtils import okhttp3.HttpUrl import okhttp3.Interceptor import okhttp3.Request import okhttp3.Response import okio.Buffer import java.io.ByteArrayInputStream import java.net.URI class SigV4Interceptor( private val credentialsProvider: AWSCredentialsProvider, private val serviceName: String ) : Interceptor { override fun intercept(chain: Interceptor.Chain): Response { val originalRequest = chain.request() if (originalRequest.url().host().contains("amazonaws.com")) { val signer = if (originalRequest.url().encodedPath().contains("@")) { // the presence of "@" indicates that it doesn't need to be double URL-encoded AWS4Signer(false) } else { AWS4Signer() } val awsRequest = toAWSRequest(originalRequest, serviceName) signer.setServiceName(serviceName) signer.sign(awsRequest, credentialsProvider.credentials) return chain.proceed(toSignedOkHttpRequest(awsRequest, originalRequest)) } return chain.proceed(originalRequest) } companion object { fun toAWSRequest(request: Request, serviceName: String): DefaultRequest<Any> { // clone the request (AWS-style) so that it can be populated with credentials val dr = DefaultRequest<Any>(serviceName) // copy request info dr.httpMethod = HttpMethodName.valueOf(request.method()) with(request.url()) { dr.resourcePath = uri().path dr.endpoint = URI.create("${scheme()}://${host()}") // copy parameters for (p in queryParameterNames()) { if (p != "") { dr.addParameter(p, queryParameter(p)) } } } // copy headers for (h in request.headers().names()) { dr.addHeader(h, request.header(h)) } // copy the request body val bodyBytes = request.body()?.let { body -> val buffer = Buffer() body.writeTo(buffer) IOUtils.toByteArray(buffer.inputStream()) } dr.content = ByteArrayInputStream(bodyBytes ?: ByteArray(0)) return dr } fun toSignedOkHttpRequest( awsRequest: DefaultRequest<Any>, originalRequest: Request ): Request { // copy signed request back into an OkHttp Request val builder = Request.Builder() // copy headers from the signed request for ((k, v) in awsRequest.headers) { builder.addHeader(k, v) } // start building an HttpUrl val urlBuilder = HttpUrl.Builder() .host(awsRequest.endpoint.host) .scheme(awsRequest.endpoint.scheme) .encodedPath(awsRequest.resourcePath) // copy parameters from the signed request for ((k, v) in awsRequest.parameters) { urlBuilder.addQueryParameter(k, v) } return builder.url(urlBuilder.build()) .method(originalRequest.method(), originalRequest.body()) .build() } } }
构建应用程序:主要活动
主活动负责初始化将向用户显示的视图。这涉及:
-
实例化Amazon Cognito
CredentialsProvider
。 -
注册签名版本 4 拦截器。
-
通过将地图指向地图样式描述符并显示相应的属性来配置地图。
MainActivity
还负责将生命周期事件转发到地图视图,使其能够在两次调用之间保留活动视口。
package aws.location.demo.maplibre import android.os.Bundle import android.widget.TextView import androidx.appcompat.app.AppCompatActivity import aws.location.demo.okhttp.SigV4Interceptor import com.amazonaws.auth.CognitoCachingCredentialsProvider import com.amazonaws.regions.Regions import com.mapbox.mapboxsdk.Mapbox import com.mapbox.mapboxsdk.maps.MapView import com.mapbox.mapboxsdk.maps.Style import com.mapbox.mapboxsdk.module.http.HttpRequestUtil import okhttp3.OkHttpClient private const val SERVICE_NAME = "geo" class MainActivity : AppCompatActivity() { private var mapView: MapView? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // configuration val identityPoolId = getString(R.string.identityPoolId) val region = getString(R.string.awsRegion) val mapName = getString(R.string.mapName) // Credential initialization val credentialProvider = CognitoCachingCredentialsProvider( applicationContext, identityPoolId, Regions.fromName(identityPoolId.split(":").first()) ) // initialize MapLibre Mapbox.getInstance(this, null) HttpRequestUtil.setOkHttpClient( OkHttpClient.Builder() .addInterceptor(SigV4Interceptor(credentialProvider, SERVICE_NAME)) .build() ) // initialize the view setContentView(R.layout.activity_main) // initialize the map view mapView = findViewById(R.id.mapView) mapView?.onCreate(savedInstanceState) mapView?.getMapAsync { map -> map.setStyle( Style.Builder() .fromUri("https://maps.geo.${region}.amazonaws.com/maps/v0/maps/${mapName}/style-descriptor") ) { style -> findViewById<TextView>(R.id.attributionView).text = style.sources.first()?.attribution } } } override fun onStart() { super.onStart() mapView?.onStart() } override fun onResume() { super.onResume() mapView?.onResume() } override fun onPause() { super.onPause() mapView?.onPause() } override fun onStop() { super.onStop() mapView?.onStop() } override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) mapView?.onSaveInstanceState(outState) } override fun onLowMemory() { super.onLowMemory() mapView?.onLowMemory() } override fun onDestroy() { super.onDestroy() mapView?.onDestroy() } }
运行此应用程序以您选择的样式显示全屏地图。此示例作为亚马逊Location Service 示例存储库的一部分提供GitHub