Android 调起第三方导航

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

相关推荐
千里马学框架3 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台3 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone3 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc3 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo3 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077003 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼3 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone3 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen3 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone3 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui