You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Studio中如何通过Google Maps API实现跨App传定位并显最短路径

实现步骤拆解

一、发起应用(发送位置)

1. 申请位置权限

在AndroidManifest.xml中添加定位权限:

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

Android 6.0+需动态请求权限:

private val REQUEST_LOCATION_PERMISSION = 1001

private fun requestLocationPermission() {
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
        != PackageManager.PERMISSION_GRANTED
    ) {
        ActivityCompat.requestPermissions(
            this,
            arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
            REQUEST_LOCATION_PERMISSION
        )
    } else {
        getCurrentLocation()
    }
}

override fun onRequestPermissionsResult(
    requestCode: Int,
    permissions: Array<out String>,
    grantResults: IntArray
) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults)
    if (requestCode == REQUEST_LOCATION_PERMISSION && grantResults.isNotEmpty()
        && grantResults[0] == PackageManager.PERMISSION_GRANTED
    ) {
        getCurrentLocation()
    }
}

2. 获取当前位置

使用Google官方推荐的FusedLocationProviderClient获取最新位置:

private lateinit var fusedLocationClient: FusedLocationProviderClient

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
}

private fun getCurrentLocation() {
    fusedLocationClient.lastLocation.addOnSuccessListener { location: Location? ->
        location?.let {
            sendLocationToReceiverApp(it.latitude, it.longitude)
        } ?: run {
            Toast.makeText(this, "无法获取当前位置,请检查定位设置", Toast.LENGTH_SHORT).show()
        }
    }
}

3. 点击按钮发送位置

定义自定义Action(需与目标应用保持一致),通过隐式Intent传递经纬度:

// 按钮点击事件绑定
btn_send_location.setOnClickListener {
    requestLocationPermission()
}

private fun sendLocationToReceiverApp(lat: Double, lng: Double) {
    val intent = Intent().apply {
        action = "com.example.SHOW_ROUTE_TO_TARGET_LOCATION"
        putExtra("DEST_LAT", lat)
        putExtra("DEST_LNG", lng)
        addCategory(Intent.CATEGORY_DEFAULT)
    }

    // 检查是否有应用能处理该Intent,避免崩溃
    if (intent.resolveActivity(packageManager) != null) {
        startActivity(intent)
    } else {
        Toast.makeText(this, "未找到目标应用", Toast.LENGTH_SHORT).show()
    }
}

二、目标应用(接收位置并显示路线)

1. 配置Intent Filter接收数据

在AndroidManifest.xml的目标Activity中添加Intent Filter,匹配发起应用的自定义Action:

<activity android:name=".RouteDisplayActivity">
    <intent-filter>
        <action android:name="com.example.SHOW_ROUTE_TO_TARGET_LOCATION" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

2. 接收并解析位置数据

在目标Activity中获取Intent传递的经纬度:

private lateinit var fusedLocationClient: FusedLocationProviderClient
private var destLat: Double = 0.0
private var destLng: Double = 0.0
private val API_KEY = "你的Google Maps API密钥"

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_route_display)
    fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)

    intent?.let {
        destLat = it.getDoubleExtra("DEST_LAT", 0.0)
        destLng = it.getDoubleExtra("DEST_LNG", 0.0)
        if (destLat != 0.0 && destLng != 0.0) {
            initMapAndFetchRoute()
        } else {
            Toast.makeText(this, "位置数据无效", Toast.LENGTH_SHORT).show()
        }
    }
}

3. 集成Google Maps SDK

3.1 配置API密钥

在Google Cloud Console启用Maps SDK for Android和Directions API,获取密钥后添加到AndroidManifest.xml:

<meta-data
    android:name="com.google.android.geo.API_KEY"
    android:value="你的Google Maps API密钥" />

3.2 布局添加地图组件

使用SupportMapFragment嵌入地图:

<fragment
    android:id="@+id/map_fragment"
    android:name="com.google.android.gms.maps.SupportMapFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

4. 获取起点位置并绘制路线

4.1 获取目标应用用户当前位置(起点)

复用发起应用的位置获取逻辑,获取用户当前位置后发起路线请求。

4.2 调用Directions API获取路线数据

添加Retrofit依赖到build.gradle:

implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'

定义API接口和数据类:

interface DirectionsApi {
    @GET("maps/api/directions/json")
    suspend fun getRoute(
        @Query("origin") origin: String,
        @Query("destination") destination: String,
        @Query("key") apiKey: String
    ): DirectionsResponse
}

data class DirectionsResponse(val routes: List<Route>)
data class Route(val overview_polyline: OverviewPolyline)
data class OverviewPolyline(val points: String)

4.3 在地图上绘制最短路线

private lateinit var map: GoogleMap

private fun initMapAndFetchRoute() {
    val mapFragment = supportFragmentManager.findFragmentById(R.id.map_fragment) as SupportMapFragment
    mapFragment.getMapAsync { googleMap ->
        map = googleMap
        getCurrentUserLocation()
    }
}

private fun getCurrentUserLocation() {
    fusedLocationClient.lastLocation.addOnSuccessListener { location: Location? ->
        location?.let { origin ->
            val originStr = "${origin.latitude},${origin.longitude}"
            val destStr = "$destLat,$destLng"
            
            CoroutineScope(Dispatchers.IO).launch {
                try {
                    val retrofit = Retrofit.Builder()
                        .baseUrl("https://maps.googleapis.com/")
                        .addConverterFactory(GsonConverterFactory.create())
                        .build()
                    val api = retrofit.create(DirectionsApi::class.java)
                    val response = api.getRoute(originStr, destStr, API_KEY)
                    
                    withContext(Dispatchers.Main) {
                        response.routes.firstOrNull()?.let { route ->
                            drawRouteOnMap(route.overview_polyline.points)
                        } ?: run {
                            Toast.makeText(this@RouteDisplayActivity, "未找到可行路线", Toast.LENGTH_SHORT).show()
                        }
                    }
                } catch (e: Exception) {
                    withContext(Dispatchers.Main) {
                        Toast.makeText(this@RouteDisplayActivity, "路线请求失败: ${e.message}", Toast.LENGTH_SHORT).show()
                    }
                }
            }
        } ?: run {
            Toast.makeText(this, "无法获取你的位置,请检查定位设置", Toast.LENGTH_SHORT).show()
        }
    }
}

private fun drawRouteOnMap(polylinePoints: String) {
    val polylineOptions = PolylineOptions()
        .color(Color.parseColor("#1A73E8"))
        .width(12f)
        .addAll(decodePolyline(polylinePoints))
    map.addPolyline(polylineOptions)
    
    // 自动调整地图视野到路线范围
    val bounds = LatLngBounds.builder()
        .include(LatLng(destLat, destLng))
        .include(LatLng(map.cameraPosition.target.latitude, map.cameraPosition.target.longitude))
        .build()
    map.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 100))
}

// 解码Polyline坐标点的工具方法
private fun decodePolyline(encoded: String): List<LatLng> {
    val poly = ArrayList<LatLng>()
    var index = 0
    val len = encoded.length
    var lat = 0
    var lng = 0

    while (index < len) {
        var b: Int
        var shift = 0
        var result = 0
        do {
            b = encoded[index++].code - 63
            result = result or (b and 0x1f shl shift)
            shift += 5
        } while (b >= 0x20)
        val dlat = if (result and 1 != 0) (result shr 1).inv() else result shr 1
        lat += dlat

        shift = 0
        result = 0
        do {
            b = encoded[index++].code - 63
            result = result or (b and 0x1f shl shift)
            shift += 5
        } while (b >= 0x20)
        val dlng = if (result and 1 != 0) (result shr 1).inv() else result shr 1
        lng += dlng

        poly.add(LatLng(lat.toDouble() / 1E5, lng.toDouble() / 1E5))
    }
    return poly
}

关键注意事项

  • 两个应用的自定义Action必须完全一致,否则无法传递数据。
  • 确保Google Cloud Console已启用所需API,且API密钥配置正确,否则路线请求会返回权限错误。
  • 处理定位权限拒绝、位置获取失败等异常场景,给用户明确提示。
  • Directions API有调用配额限制,需根据实际使用情况调整配额。

内容的提问来源于stack exchange,提问作者user12206363

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 20:01:02