Android 调起第三方导航
一、功能说明
实现 Android 应用内调起高德、百度、腾讯地图导航,包含:
- 自动坐标转换(WGS84/GCJ02/BD09)
- Android 11+ 包可见性适配
- 安装检测 + 自动降级
- 驾车/步行/骑行导航
二、权限配置(必须)
AndroidManifest.xml
xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Android 11+ 包可见性声明 -->
<queries>
<package android:name="com.autonavi.minimap" />
<package android:name="com.baidu.BaiduMap" />
<package android:name="com.tencent.map" />
</queries>
<application>
<activity android:name=".MainActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
三、坐标转换工具类
CoordinateConverter.kt
kotlin
import kotlin.math.*
object CoordinateConverter {
private const val PI = 3.1415926535897932384626
private const val A = 6378245.0
private const val EE = 0.00669342162296594323
fun wgs84ToGcj02(lng: Double, lat: Double): DoubleArray {
if (outOfChina(lng, lat)) return doubleArrayOf(lng, lat)
var dLat = transformLat(lng - 105.0, lat - 35.0)
var dLng = transformLng(lng - 105.0, lat - 35.0)
val radLat = lat / 180.0 * PI
var magic = sin(radLat)
magic = 1 - EE * magic * magic
val sqrtMagic = sqrt(magic)
dLat = (dLat * 180.0) / ((A * (1 - EE)) / (magic * sqrtMagic) * PI)
dLng = (dLng * 180.0) / (A / sqrtMagic * cos(radLat) * PI)
return doubleArrayOf(lng + dLng, lat + dLat)
}
fun gcj02ToBd09(lng: Double, lat: Double): DoubleArray {
val z = sqrt(lng * lng + lat * lat) + 0.00002 * sin(lat * PI * 3000.0 / 180.0)
val theta = atan2(lat, lng) + 0.000003 * cos(lng * PI * 3000.0 / 180.0)
return doubleArrayOf(z * cos(theta) + 0.0065, z * sin(theta) + 0.006)
}
fun bd09ToGcj02(lng: Double, lat: Double): DoubleArray {
val x = lng - 0.0065
val y = lat - 0.006
val z = sqrt(x * x + y * y) - 0.00002 * sin(y * PI * 3000.0 / 180.0)
val theta = atan2(y, x) - 0.000003 * cos(x * PI * 3000.0 / 180.0)
return doubleArrayOf(z * cos(theta), z * sin(theta))
}
private fun outOfChina(lng: Double, lat: Double): Boolean {
return lng < 72.004 || lng > 137.8347 || lat < 0.8293 || lat > 55.8271
}
private fun transformLat(x: Double, y: Double): Double {
var ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * sqrt(abs(x))
ret += (20.0 * sin(6.0 * x * PI) + 20.0 * sin(2.0 * x * PI)) * 2.0 / 3.0
ret += (20.0 * sin(y * PI) + 40.0 * sin(y / 3.0 * PI)) * 2.0 / 3.0
ret += (160.0 * sin(y / 12.0 * PI) + 320 * sin(y * PI / 30.0)) * 2.0 / 3.0
return ret
}
private fun transformLng(x: Double, y: Double): Double {
var ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * sqrt(abs(x))
ret += (20.0 * sin(6.0 * x * PI) + 20.0 * sin(2.0 * x * PI)) * 2.0 / 3.0
ret += (20.0 * sin(x * PI) + 40.0 * sin(x / 3.0 * PI)) * 2.0 / 3.0
ret += (150.0 * sin(x / 12.0 * PI) + 300.0 * sin(x / 30.0 * PI)) * 2.0 / 3.0
return ret
}
}
四、导航工具类(核心)
NavigationUtils.kt
kotlin
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.widget.Toast
object NavigationUtils {
private const val AMAP = "com.autonavi.minimap"
private const val BAIDU = "com.baidu.BaiduMap"
private const val TENCENT = "com.tencent.map"
fun startNavigation(
context: Context,
wgs84Lat: Double,
wgs84Lng: Double,
name: String,
navType: Int = 0,
prefer: Int = 0
) {
val (lat, lng) = when (prefer) {
0, 2 -> {
val gcj = CoordinateConverter.wgs84ToGcj02(wgs84Lng, wgs84Lat)
gcj[1] to gcj[0]
}
1 -> {
val gcj = CoordinateConverter.wgs84ToGcj02(wgs84Lng, wgs84Lat)
val bd = CoordinateConverter.gcj02ToBd09(gcj[0], gcj[1])
bd[1] to bd[0]
}
else -> wgs84Lat to wgs84Lng
}
val intent = when (prefer) {
0 -> amapIntent(lat, lng, name, navType)
1 -> baiduIntent(lat, lng, name, navType)
2 -> tencentIntent(lat, lng, name, navType)
else -> amapIntent(lat, lng, name, navType)
}
val pkg = when (prefer) {
0 -> AMAP
1 -> BAIDU
2 -> TENCENT
else -> AMAP
}
if (isInstalled(context, pkg)) {
context.startActivity(intent)
} else {
tryFallback(context, lat, lng, name, navType, prefer)
}
}
private fun amapIntent(lat: Double, lng: Double, name: String, type: Int): Intent {
val t = when (type) { 0 -> "0"; 1 -> "1"; 2 -> "2"; else -> "0" }
val uri = Uri.parse("amapuri://route/plan/?dlat=$lat&dlng=$lng&dname=$name&dev=0&t=$t")
return Intent(Intent.ACTION_VIEW, uri)
}
private fun baiduIntent(lat: Double, lng: Double, name: String, type: Int): Intent {
val mode = when (type) { 0 -> "driving"; 1 -> "walking"; 2 -> "riding"; else -> "driving" }
val uri = Uri.parse("baidumap://map/direction/?destination=latlng:$lat,$lng|name:$name&mode=$mode")
return Intent(Intent.ACTION_VIEW, uri)
}
private fun tencentIntent(lat: Double, lng: Double, name: String, type: Int): Intent {
val t = when (type) { 0 -> "drive"; 1 -> "walk"; 2 -> "bike"; else -> "drive" }
val uri = Uri.parse("qqmap://map/routeplan?type=$t&to=$name&tocoord=$lat,$lng")
return Intent(Intent.ACTION_VIEW, uri)
}
private fun isInstalled(context: Context, pkg: String): Boolean {
return try {
context.packageManager.getApplicationInfo(pkg, 0)
true
} catch (e: Exception) {
false
}
}
private fun tryFallback(ctx: Context, lat: Double, lng: Double, name: String, type: Int, prefer: Int) {
when (prefer) {
0 -> when {
isInstalled(ctx, BAIDU) -> ctx.startActivity(baiduIntent(lat, lng, name, type))
isInstalled(ctx, TENCENT) -> ctx.startActivity(tencentIntent(lat, lng, name, type))
else -> toast(ctx)
}
1 -> when {
isInstalled(ctx, AMAP) -> ctx.startActivity(amapIntent(lat, lng, name, type))
isInstalled(ctx, TENCENT) -> ctx.startActivity(tencentIntent(lat, lng, name, type))
else -> toast(ctx)
}
2 -> when {
isInstalled(ctx, AMAP) -> ctx.startActivity(amapIntent(lat, lng, name, type))
isInstalled(ctx, BAIDU) -> ctx.startActivity(baiduIntent(lat, lng, name, type))
else -> toast(ctx)
}
else -> toast(ctx)
}
}
private fun toast(context: Context) {
Toast.makeText(context, "请安装高德/百度/腾讯地图", Toast.LENGTH_SHORT).show()
}
}
五、调用示例
kotlin
// 天安门 WGS84 坐标
NavigationUtils.startNavigation(
context = this,
wgs84Lat = 39.908823,
wgs84Lng = 116.397470,
name = "天安门",
navType = 0, // 0驾车 1步行 2骑行
prefer = 0 // 0高德 1百度 2腾讯
)
六、常见问题
1. 已安装高德却提示未安装
原因 :Android 11+ 包可见性限制
解决 :必须在 AndroidManifest.xml 添加 <queries>
2. 导航位置偏移
原因:坐标系不统一
- 高德 / 腾讯:GCJ02
- 百度:BD09
- GPS 原始:WGS84
解决 :使用CoordinateConverter自动转换
3. 调起失败
检查包名:
- 高德:
com.autonavi.minimap - 百度:
com.baidu.BaiduMap - 腾讯:
com.tencent.map
七、测试命令
bash
adb shell pm list packages | grep com.autonavi.minimap