更新记录
1.0.5(2026-10-19) 下载此版本
修复一些bug
1.0.4(2026-10-19) 下载此版本
- 修复:半屏嵌入时导航视图顶部多出一条空白(Android 导航状态栏占位 + 系统窗口内边距),创建视图时关闭
setNaviStatusBarEnabled(false)与setFitsSystemWindows(false)
1.0.3(2026-10-19) 下载此版本
修复一些bug
查看更多平台兼容性
uni-app(5.26)
| Vue2 | Vue3 | Chrome | Safari | app-vue | app-nvue | Android | iOS | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|
| √ | √ | - | - | × | √ | √ | √ | - |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| - | - | - | - | - | - | - | - | - | - | - | - |
uni-app x(5.26)
| Chrome | Safari | Android | iOS | 鸿蒙 | 微信小程序 |
|---|---|---|---|---|---|
| - | - | - | - | - | - |
其他
| 多语言 | 暗黑模式 | 宽屏模式 | 蒸汽模式 |
|---|---|---|---|
| × | × | × | √ |
lj-amap-nav 高德导航(半屏)
在 uni-app 的 nvue 页面里嵌入一块半屏高德导航地图:导航视图按你指定的尺寸铺在页面上半屏,下半屏继续放自己的业务 UI,两种内容同屏共存。
支持 驾车 / 骑行 / 步行 三种方式、一键导航(自动取当前位置)、模拟导航、内置语音播报、单次 / 持续定位。
<lj-amap-nav ref="navi" style="height:400px" @location="onLocation"></lj-amap-nav>
this.$refs.navi.startRouteFromCurrent(39.909187, 116.397451, false) // 从当前位置导航到天安门
Key 怎么给:推荐写进插件常量
utssdk/app-android/index.vue的API_KEY(Android)、utssdk/app-ios/index.vue的API_KEY(iOS),页面不用传,见「4.3」。用:apikey属性也可以,但 nvue 下不保证拿到,见「十二」。打包必读:云打包与本地打包的依赖接法完全不同,接错的表现都是「一进页面就闪退」(
NoClassDefFoundError: Lcom/amap/api/maps/MapsInitializer),详见「十一」。
目录
| 章节 | 内容 |
|---|---|
| 一 | 特性 |
| 二 | 平台支持 |
| 三 | 环境要求 |
| 四 | 接入步骤(4 步) |
| 五 | 组件属性 |
| 六 | 方法 API |
| 七 | 事件 |
| 八 | 错误码 |
| 九 | 典型场景代码 |
| 十 | 使用限制与注意事项 |
| 十一 | 打包注意事项(云打包 / 本地打包) |
| 十二 | Key 取不到(WARNING 未传入 apiKey / code=13)的排查 |
| 十三 | 常见问题 FAQ |
| 十四 | 版本记录 |
| 十五 | 许可与声明 |
只想尽快跑起来:看「三 → 四」;查参数跳「五、六、七」;闪退/黑屏跳「十一、十二、十三」。
一、特性
| 特性 | 说明 |
|---|---|
| 半屏 / 任意尺寸嵌入 | 高度自己定,上半屏、上下分屏、三分之一屏都行 |
| 三种导航方式 | 驾车 / 骑行 / 步行,同一个组件内切换,无需重建页面 |
| 一键导航 | 只给终点,起点自动取当前位置 |
| 模拟导航 | 不用真实移动也能演示导航过程,适合室内调试 |
| 内置语音播报 | 使用高德自带语音,无需接入第三方 TTS |
| 自动就近 | 骑行 / 步行终点过远时,自动把终点拉近到起点附近,避免算路失败 |
| 单次 / 持续定位 | locateStart() 拿一次坐标;startContinuousLocation() 持续上报 |
| 事件回传 | 定位结果与运行日志都以组件事件回传,可实时显示在界面上 |
| 退出即清理 | 页面销毁自动停止导航、停止语音、释放定位 |
二、平台支持
| 平台 | 支持 |
|---|---|
| App-Android | ✅ |
| App-iOS | ✅ |
| App-Harmony | ❌ |
| H5 / 小程序 / App-vue 页面 | ❌ |
- 组件只能用在 nvue 页面(
.nvue文件); - 内置能力版本:navi 11.2.100 / 3dmap 11.2.100 / location 11.2.100 / search 9.8.1。
三、环境要求
| 项 | 要求 |
|---|---|
| HBuilderX | 4.36 及以上 |
| 运行方式 | 必须使用自定义基座或正式打包,标准基座不含插件原生库 |
| Android | minSdkVersion 23 及以上,仅支持 arm64-v8a |
| 页面类型 | 必须是 nvue 页面 |
| 高德 Key | 必须自行申请(Android、iOS 各一个,不通用) |
| 打包方式 | 云打包从 Maven 自动拉取(插件内不携带 jar);本地(离线)打包需自行准备 jar / so / assets,见「十一」 |
每次改动
uni_modules/lj-amap-nav/下的文件,都要重新制作一次自定义基座;只改自己的pages/不用。
四、接入步骤(4 步)
4.1 导入插件
插件市场点「导入」,插件会出现在项目的 uni_modules/lj-amap-nav/ 下。
4.2 申请高德 Key
到高德开放平台 → 控制台 → 创建应用 → 添加 Key:
| 平台 | 必填信息 | 注意 |
|---|---|---|
| Android | 包名 + 签名 SHA1 | 包名用真机上的实际包名;SHA1 用正在运行的那个基座/安装包的签名 |
| iOS | Bundle Identifier | 与证书里的 BundleId 一致 |
- Key 绑错不会报错,只表现为「地图黑屏 / 算路失败(
code=7)」; - 调试基座换了证书,SHA1 会变,Key 也要跟着更新;
- 一个 Key 即可覆盖该 App 的地图 / 定位 / 算路 / 导航能力。
4.3 配置 Key(三种方式,选一个)
方式 A:写进插件常量(推荐,最稳)
// utssdk/app-android/index.vue 顶部
const API_KEY = '你的 Android Key'
// utssdk/app-ios/index.vue 顶部
const API_KEY = '你的 iOS Key'
- 发布版这两个常量留空(避免泄露),使用者自己填;
- 生效日志:
apiKey 来源=builtin; - 注意:插件升级会覆盖这两个文件,升级后需要重新填。
方式 B:交给高德 SDK 读清单(只影响 Android,本地打包推荐)
在 AndroidManifest.xml 里写:
<meta-data android:name="com.amap.api.v2.apikey" android:value="你的高德Key" />
- 插件自带的那份
utssdk/app-android/AndroidManifest.xml不再声明这个 meta-data(留空会和宿主 App 的 Key 合并冲突),需要用方式 B 的话自己加进去; - 云打包:加在插件这份
AndroidManifest.xml(会自动合并); - 本地(离线)打包不会合并插件清单,必须手写进 AS 工程的
src/main/AndroidManifest.xml(见 11.3 ③); - 插件当前逻辑:只有拿到非空 Key 才会
setApiKey,不会把清单里的 Key 冲掉,两种方式可以并存。
好处:Key 跟着宿主工程走,插件升级不会被覆盖,所以本地打包优先推荐方式 B。
方式 C:页面用属性传(写法最省事,但 nvue 下不保证生效)
<lj-amap-nav ref="navi" style="height:400px" :apikey="amapKey"
@location="onLocation" @log="onLog"></lj-amap-nav>
data() {
const platform = uni.getSystemInfoSync().platform
const amapKey = platform === 'ios' ? '你的 iOS Key' : '你的 Android Key'
return { amapKey: amapKey }
}
- 属性名必须全小写
apikey(api-key/apiKey更容易传不进); - 若日志出现
WARNING 未传入 apiKey,说明属性没拿到,原因与解法见「十二」。
三种方式选一个即可。判断生效结果看启动日志:
apiKey 来源=builtin(方式 A/B)或apiKey 来源=apikey(方式 C)。⚠️ 对外发布的插件包不要提交真实 Key。发布版
API_KEY保持'',由使用者自己填或走属性。
4.4 打自定义基座并运行
- 运行 → 制作自定义调试基座(Android / iOS 各提交一次);
- 运行 → 运行基座选择 → 勾「自定义调试基座」;
- 建议先卸载手机上旧的同名 App,再运行到真机(避免装的是旧包)。
正常启动日志(按顺序):
[lj-amap-nav] [life] NVLoad 开始
[lj-amap-nav] [life] apiKey 来源=builtin ← 或 =apikey
[lj-amap-nav] [life] 高德 Key 与隐私合规已设置
[lj-amap-nav] [life] 地图初始化成功
[lj-amap-nav] [life] 导航实例创建成功
[lj-amap-nav] [view] 导航视图加载完成
- 出现
WARNING 未传入 apiKey→ Key 为空,算路必然code=13,见「十二」; - 地图黑屏 → Key 与「包名 + SHA1 / BundleId」不匹配,见「8、错误码」。
五、组件属性
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
apikey |
String | 与方式 A 二选一 | 高德 Key(仅方式 C 使用)。见 5.1 |
style |
String | 是 | 必须给明确宽高,例如 style="height:400px";不支持 flex:1,不指定尺寸组件不显示 |
ref |
String | 是 | 用于拿组件实例调方法:this.$refs.navi.xxx() |
5.1 apikey
| 项 | 说明 |
|---|---|
| 取值 | Android 传 Android Key、iOS 传 iOS Key(两把不通用) |
| 写法 | :apikey="变量" 或 apikey="你的Key",全小写 |
| 可靠性 | nvue 下属性注入可能晚于组件初始化,稳妥做法是改用 API_KEY(方式 A) |
| 是否生效 | 日志 apiKey 来源=apikey 为生效;来源=builtin 表示用的是插件常量 |
5.2 style
- 必须给确定尺寸:
style="height:400px"、style="width:750rpx;height:560rpx"; - 不支持
flex:1; - 组件尺寸在创建时确定,运行中修改
:style不会重新布局 —— 先算好尺寸再用v-if挂载; - 想做「半屏」:
uni.getSystemInfoSync().windowHeight / 2。
5.3 ref
所有方法都通过 this.$refs.navi.xxx() 调用。拿不到实例时调用会静默失败,请确认 ref 名称一致。
六、方法 API
6.1 方法总览
| 分类 | 方法 | 说明 |
|---|---|---|
| 初始化 | tryInit() / pageShow() / pageHide() |
页面生命周期联动 |
| 导航方式 | setNaviType(t) |
切换 驾车 / 骑行 / 步行 |
| 算路 | startRoute(sLat,sLng,eLat,eLng,emulator) |
指定起终点 |
| 算路 | startRouteFromCurrent(eLat,eLng,emulator) |
一键导航(起点取当前位置) |
| 算路 | stopRoute() |
停止导航 |
| 定位 | locateStart() |
单次定位 |
| 定位 | startContinuousLocation(intervalMs) / stopContinuousLocation() |
持续定位 / 停止 |
| 定位 | isContinuousLocationRunning() |
是否持续定位中 |
| 定位 | useLastKnownLocation() |
用最近一次已知位置兜底 |
| 定位 | hasLocationPermission() / requestLocationPermission() |
查权限 / 申请权限(仅 Android) |
| 状态 | getLastLocation() / hasLocationReady() |
最近坐标 / 是否已定位 |
| 状态 | getLastTts() / getLastError() / getNaviType() / clearError() |
最近播报 / 最近错误 / 当前方式 / 清错误 |
| 日志 | setLogLevel(level) / getLogLevel() |
日志级别 |
| 回调 | onLocationUpdate(cb) / onTtsUpdate(cb) / onErrorUpdate(cb) |
函数式回调(备选,见 6.7) |
读取类方法(
getLastLocation()等)的同步返回值在 nvue 下不可靠,只作调试参考;要拿数据请用事件(第七节)。
6.2 初始化与生命周期
| 方法 | 参数 | 返回 | 说明 |
|---|---|---|---|
tryInit() |
— | void | 初始化 / 补发视图生命周期,内部幂等,可重复调用 |
pageShow() |
— | void | 页面可见时调用(与 tryInit() 等价,二选一) |
pageHide() |
— | void | 页面隐藏时调用,不会打断正在进行的导航 |
onReady() { this.$refs.navi.tryInit() },
onShow() { this.$refs.navi.tryInit() },
onHide() { this.$refs.navi.pageHide() }
6.3 导航方式与算路
setNaviType(t)
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
t |
String | 是 | 'drive' 驾车(默认)/ 'ride' 骑行 / 'walk' 步行 |
正在导航时切换会先停止当前导航,切换后需要重新调用 startRoute*()。
this.$refs.navi.setNaviType('ride')
startRoute(sLat, sLng, eLat, eLng, emulator)
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
sLat |
Number | 是 | 起点纬度 |
sLng |
Number | 是 | 起点经度 |
eLat |
Number | 是 | 终点纬度 |
eLng |
Number | 是 | 终点经度 |
emulator |
Boolean | 是 | true 模拟导航 / false 实时导航 |
this.$refs.navi.startRoute(39.909187, 116.397451, 39.993185, 116.396612, false)
坐标必须是 gcj02。来自 GPS(wgs84)的坐标请先转换,否则偏移或算路失败。
startRouteFromCurrent(eLat, eLng, emulator) ⭐ 最常用
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
eLat |
Number | 是 | 终点纬度 |
eLng |
Number | 是 | 终点经度 |
emulator |
Boolean | 是 | true 模拟导航 / false 实时导航 |
流程:已有定位 → 直接算路;无定位 → 申请权限并定位 → 定位成功自动算路 → 算路成功自动起导航。首次安装也不需要点第二次。
this.$refs.navi.startRouteFromCurrent(39.909187, 116.397451, false)
stopRoute()
停止导航、停止语音、释放定位(持续定位一起停)。建议在 onUnload 里调用。
onUnload() { this.$refs.navi.stopRoute() }
6.4 定位
locateStart()
单次定位,结果通过 @location 回传。未授权时自动弹系统授权框,允许后自动继续。
this.$refs.navi.locateStart()
startContinuousLocation(intervalMs)
| 参数 | 类型 | 必填 | 默认 | 说明 |
|---|---|---|---|---|
intervalMs |
Number | 否 | 2000 |
上报间隔(毫秒)。仅 Android 按此间隔;iOS 按系统回调频率。传 <=0 按 2000 |
每次上报都触发一次 @location;与单次定位共用同一事件通道;调用 locateStart() 会切回单次模式。
this.$refs.navi.startContinuousLocation(2000)
stopContinuousLocation()
停止持续定位,可再次 startContinuousLocation() 恢复。
isContinuousLocationRunning()
返回 Boolean,是否正在持续定位(同步返回值,仅调试参考)。
useLastKnownLocation()
用最近一次已知位置立即补发一次 @location(从未定位过则无动作)。
权限(仅 Android)
| 方法 | 返回 | 说明 |
|---|---|---|
hasLocationPermission() |
Boolean | 是否已授予定位权限 |
requestLocationPermission() |
void | 弹系统授权框申请权限 |
iOS 没有「主动申请」这一步,首次定位时系统自动弹窗。
页面不要自己调
plus.android.requestPermissions,拿不到回调;用本插件方法即可。
6.5 状态读取
| 方法 | 返回 | 说明 |
|---|---|---|
getLastLocation() |
String | 最近坐标 "纬度,经度",未定位为空串 |
hasLocationReady() |
Boolean | 是否已取得过定位 |
getLastTts() |
String | 最后一条播报文本(iOS 当前返回空串) |
getLastError() |
String | 最后一次错误描述 |
getNaviType() |
String | 当前方式:drive / ride / walk |
clearError() |
void | 清空错误记录 |
6.6 日志
| 方法 | 参数 | 返回 | 说明 |
|---|---|---|---|
setLogLevel(level) |
Number | void | 0 关闭 / 1 简洁 / 2 详细,默认 2 |
getLogLevel() |
— | Number | 当前级别 |
日志同时走两条通道:控制台(统一 [lj-amap-nav] 前缀)与 @log 事件(可直接显示在界面上)。
6.7 函数式回调(备选)
| 方法 | 回调参数 | 说明 |
|---|---|---|
onLocationUpdate(cb) |
text: String |
定位成功回调(持续定位每次上报都回调) |
onTtsUpdate(cb) |
text: String |
播报文本(Android 有内容,iOS 暂不回调) |
onErrorUpdate(cb) |
text: String |
算路失败、自动就近等错误 |
nvue 下把函数传进组件方法可能不生效,这三个仅作备选,推荐用事件。
七、事件
7.1 @location(定位结果)⭐
| 字段 | 类型 | 说明 |
|---|---|---|
ok |
Boolean | true 成功;false 失败 |
text |
String | "纬度,经度",失败时为空串 |
lat |
Number | 纬度,失败时 0 |
lng |
Number | 经度,失败时 0 |
err |
String | 失败原因,例如 定位失败 code=12 info=缺少定位权限 |
count |
Number | 本次运行第几次成功定位(持续定位递增) |
触发时机:单次定位成功一次;持续定位每次上报一次;失败也会触发 ok=false(务必把 err 显示出来)。
两端读取位置不同,推荐这样写以同时兼容:
onLocation(e) {
let d = e.detail
if (d == null || Object.keys(d).length === 0) {
d = e // iOS 上数据在事件对象顶层
}
if (d.ok === false) {
this.errText = d.err
return
}
this.locText = d.text // "39.909187,116.397451"
this.lat = d.lat
this.lng = d.lng
}
7.2 @log(运行日志)
| 字段 | 类型 | 说明 |
|---|---|---|
line |
String | 完整日志行,如 10:17:21 [loc] 定位成功 39.909187,116.397451 |
level |
Number | 1 简洁 / 2 详细 |
seq |
Number | 自增序号,可用于排序 |
读取方式同 @location(Android e.detail.line,iOS e.line)。把 line 直接显示在页面上,现场排查不用连电脑:
onLog(e) {
let d = e.detail
if (d == null || Object.keys(d).length === 0) d = e
if (d.line) {
this.logLines.unshift(d.line)
if (this.logLines.length > 50) this.logLines.pop()
}
}
7.3 事件绑定完整示例
<lj-amap-nav ref="navi" style="height:400px"
@location="onLocation" @log="onLog"></lj-amap-nav>
八、错误码
@location 的 err 与 @log 里会带错误码:
| 码 | 含义 | 处理 |
|---|---|---|
-2 |
未授权定位权限(申请中 / 被拒绝) | 允许后自动继续;拒绝则去系统设置开启 |
-3 |
定位初始化失败 | 多为 Key 或网络问题,看 err 详情 |
-4 |
定位未就绪 | 重试一次;持续出现请附日志反馈 |
7 |
Key 错误 | Android Key 未绑当前「包名 + SHA1」;iOS Key 未绑当前 BundleId |
12 |
缺少定位权限 | 检查系统权限开关(含「精确位置」) |
13 |
Key 非法或为空 | Key 没生效:WARNING 未传入 apiKey,见「十二」 |
19 / 20 |
骑行 / 步行起终点距离太长 | 插件已自动就近(骑行 20 km / 步行 5 km),一般无需处理 |
29 |
导航状态冲突 | 切换导航方式时插件会先停止当前导航,正常不会出现 |
九、典型场景代码
9.1 一键导航到指定点
this.$refs.navi.startRouteFromCurrent(39.909187, 116.397451, false)
9.2 指定起点 + 模拟导航(室内调试)
this.$refs.navi.startRoute(39.909187, 116.397451, 39.993185, 116.396612, true)
9.3 骑行 / 步行
this.$refs.navi.setNaviType('ride') // 或 'walk'
this.$refs.navi.startRouteFromCurrent(39.909187, 116.397451, false)
终点过远时插件会自动就近到起点附近(骑行约 1.5 km / 步行约 0.6 km),日志会有提示。
9.4 持续定位并展示轨迹
<lj-amap-nav ref="navi" style="height:400px" @location="onLocation"></lj-amap-nav>
<view class="btns">
<view class="btn" @click="startLoc">开始定位</view>
<view class="btn" @click="stopLoc">停止定位</view>
</view>
<text v-for="(p, i) in track" :key="i">{{ p }}</text>
export default {
data() {
return { track: [] }
},
methods: {
onLocation(e) {
let d = e.detail
if (d == null || Object.keys(d).length === 0) d = e
if (d.ok === false) {
console.log('定位失败:' + d.err)
return
}
this.track.unshift(d.text)
if (this.track.length > 6) this.track.pop()
},
startLoc() { this.$refs.navi.startContinuousLocation(2000) },
stopLoc() { this.$refs.navi.stopContinuousLocation() }
},
onUnload() { this.$refs.navi.stopContinuousLocation() }
}
9.5 页面生命周期与清理
onReady() { this.$refs.navi.tryInit() },
onShow() { this.$refs.navi.tryInit() },
onHide() { this.$refs.navi.pageHide() },
onUnload() { this.$refs.navi.stopRoute() }
十、使用限制与注意事项
- 只能用在 nvue 页面,
app-vue页面不支持; - 必须给组件明确尺寸,
flex:1无效; - 必须使用自定义基座,标准基座无法运行;改动插件目录后需重做基座;
- 坐标系为 gcj02,其它坐标系请先转换;
- 权限:首次定位会弹系统授权框,允许后自动继续;用户拒绝需到系统设置开启;
- 后台定位未开启:App 切到后台后定位会被系统挂起;
- Android 仅支持
arm64-v8a,x86 模拟器无法运行; - 组件对象不要手动创建 / 销毁,用
v-if控制显隐; - Key 在组件创建时一次性生效,改了要重新进入页面。
十一、打包注意事项(云打包 / 本地打包)
插件内含高德 SDK(Android:AMapNavi / AMap3DMap / AMapLocation;iOS:AMapNavi + AMapLocation), 两种打包方式的依赖接法完全不同。接错的统一表现:一进页面就闪退, 崩溃日志
NoClassDefFoundError: Failed resolution of: Lcom/amap/api/maps/MapsInitializer;。
11.1 结论先行
| 打包方式 | 高德 SDK 怎么进包 | 关键点 |
|---|---|---|
| 云打包(制作自定义调试基座 / 云打包正式包) | 由插件 utssdk/app-android/config.json 的 dependencies(maven)自动拉取 |
插件不携带 jar,全靠这个坐标;插件体积仅几十 KB |
| 本地打包(离线 SDK + Android Studio) | ① 推荐:AS 工程用 gradle 依赖同样从 Maven 拉;② 离线:自己放 jar / so / assets | config.json 的 maven 坐标对本地打包无效(那套依赖只给云端构建用) |
通用硬性条件:
- 仅 nvue 页面(app-vue 页面不支持);
- Android 仅 arm64-v8a;
minSdkVersion ≥ 23; - 必须用自定义基座(标准基座不含插件原生库);
- 必须自备高德 Key:Android 绑「包名 + 签名 SHA1」,iOS 绑 BundleId(绑错不报错,只表现为黑屏 /
code=7)。
11.2 云打包
① 在插件 config.json 里声明 maven 依赖
uni_modules/lj-amap-nav/utssdk/app-android/config.json:
{
"dependencies": [
"com.amap.api:navi-3dmap-location-search:11.2.100_3dmap11.2.100_loc11.2.100_sea9.8.1"
],
"project": {
"repositories": [
"maven { url 'https://maven.aliyun.com/repository/public' }",
"maven { url 'https://repo1.maven.org/maven2' }"
]
},
"abis": [
"arm64-v8a"
],
"minSdkVersion": 23
}
- 这是合包坐标(导航 + 3D 地图 + 定位 + 搜索,一个 jar 全含),与插件
libs/里的 jar 文件名完全对应,不会出现版本错配; - 该坐标已在 Maven Central 上核实存在(jar 约 95.8 MB / 100,458,122 字节,MD5
09D67A8FC86E1DEDE2E1CFAB1C057C58); - ⚠️ 不要拆成
com.amap.api:navi-3dmap:11.2.100_3dmap11.2.100:Maven Central 上navi-3dmap这个 artifact 最高只到10.0.800_3dmap10.0.800(2024-08),没有 11.x,写 11.x 会直接Could not find com.amap.api:navi-3dmap:...构建失败; com.amap.api:location:11.2.100单独存在,但合包已包含它,不需要再单独声明;- 若你的构建环境会把
libs/*.jar也编进 dex,请先删除libs/里的 jar,避免Duplicate class com.amap.api.*(详见 11.5 表格)。
② 插件不携带 jar(插件体积与 APK 体积双赢)
插件已删除 utssdk/app-android/libs/*.jar(原 95.8 MB)。原因:打包时 HBuilderX 会把 uni_modules/** 整体拷进 APK 的 assets/uni_modules/.../libs/,那份 jar 不参与编译、运行时也不在 classpath,却让 APK 白白大 94.6 MB。
实测对比(同一个离线工程):debug APK 180.6 MB → 删除 jar 后 ≈86 MB,与 debug / release 无关。
- 所以云打包以
dependencies为准; - 做离线打包时,自行从 Maven 下载同一个 jar(地址与 MD5 见 11.3)。
③ 宿主工程的 manifest.json
- 权限(本插件需要的 10 个):
INTERNET、ACCESS_NETWORK_STATE、ACCESS_WIFI_STATE、CHANGE_WIFI_STATE、ACCESS_FINE_LOCATION、ACCESS_COARSE_LOCATION、ACCESS_LOCATION_EXTRA_COMMANDS、FOREGROUND_SERVICE、FOREGROUND_SERVICE_LOCATION、WAKE_LOCK app-plus.distribute.android下:"minSdkVersion": 23、"abiFilters": ["arm64-v8a"]- iOS:
privacyDescription补三条定位说明 (NSLocationWhenInUseUsageDescription/NSLocationAlwaysAndWhenInUseUsageDescription/NSLocationAlwaysUsageDescription)
④ 打完怎么验证依赖真的进包了
把基座 APK 当 zip 打开看两点:
classes*.dex里能搜到字符串com/amap/api/maps/MapsInitializer→ 依赖已进包;- 若 jar 只出现在
assets/uni_modules/lj-amap-nav/utssdk/app-android/libs/、dex 里搜不到 → 没编译进去,运行时必然闪退。
11.3 本地打包(离线 SDK + Android Studio)
以 DCloud 离线工程为例(模块名一般是 simpleDemo,路径自定)。
① 依赖怎么进包:二选一
方案 A(推荐,需联网):AS 工程直接用 gradle 依赖,不用手工放 jar 和 so
// 根 build.gradle 的 allprojects.repositories 里补仓库
repositories {
google()
mavenCentral()
maven { url 'https://maven.aliyun.com/repository/public' }
}
// simpleDemo/build.gradle 的 dependencies 里加
implementation 'com.amap.api:navi-3dmap-location-search:11.2.100_3dmap11.2.100_loc11.2.100_sea9.8.1'
lib/arm64-v8a/*.so会自动打进 APK,不用再放jniLibs;- ⚠️ 该坐标是 jar(不是 aar),jar 里的
assets/不会被自动合并 → 仍需按方案 B 的第 3 行手工合并 assets,否则地图黑屏、语音异常。
方案 B(完全离线):三样东西要放对位置
| 放到哪里 | 放什么 | 说明 |
|---|---|---|
<AS工程>/simpleDemo/libs/ |
整个 navi-3dmap-location-search-...jar |
build.gradle 里通常已有 fileTree(dir:'libs', include:['*.aar','*.jar']),不用改依赖块 |
<AS工程>/simpleDemo/src/main/jniLibs/arm64-v8a/ |
jar 里 lib/arm64-v8a/ 下的 7 个 so |
必须解出来:jar 内的 so 不会被加载(libAMapSDK_NAVI_v11_2_100.so 约 64.6 MB) |
<AS工程>/simpleDemo/src/main/assets/ |
jar 里 assets/ 下全部文件(约 1657 个) |
导航引擎资源(eyrie/Localizable/map_assets/navi/svg 等),缺了会黑屏、语音异常 |
jar 内 assets 的顶层目录都是高德自己的,与 uni-app 的 apps/、dcloud_control.xml 不冲突,可直接合并。
一键解压脚本(PowerShell,改前两个变量即可):
Add-Type -AssemblyName System.IO.Compression.FileSystem
$jar = '你下载的\navi-3dmap-location-search-11.2.100_3dmap11.2.100_loc11.2.100_sea9.8.1.jar'
$proj = '你的离线工程\simpleDemo'
Copy-Item $jar (Join-Path $proj 'libs') -Force
$assetsOut = Join-Path $proj 'src\main\assets'
$jniOut = Join-Path $proj 'src\main\jniLibs\arm64-v8a'
New-Item -ItemType Directory -Force -Path $jniOut | Out-Null
$zip = [System.IO.Compression.ZipFile]::OpenRead($jar)
foreach ($e in $zip.Entries) {
$n = $e.FullName
if ($n.EndsWith('/')) { continue }
if ($n -like 'assets/*') {
$t = Join-Path $assetsOut $n
New-Item -ItemType Directory -Force -Path (Split-Path $t) | Out-Null
[System.IO.Compression.ZipFileExtensions]::ExtractToFile($e, $t, $true)
} elseif ($n -like 'lib/arm64-v8a/*.so') {
[System.IO.Compression.ZipFileExtensions]::ExtractToFile($e, (Join-Path $jniOut (Split-Path $n -Leaf)), $true)
}
}
$zip.Dispose()
② <AS工程>/simpleDemo/build.gradle
defaultConfig {
applicationId "com.xxx.xxx"
minSdkVersion 23 // 插件要求 ≥ 23
multiDexEnabled true // 高德类较多,务必开启
ndk {
abiFilters 'arm64-v8a' // 插件只带 arm64 的 so
}
}
③ <AS工程>/simpleDemo/src/main/AndroidManifest.xml
插件自带的 utssdk/app-android/AndroidManifest.xml(权限 + APSService + 高德 Key 的 meta-data)本地打包不会自动合并,需手动补:
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<application ...>
<!-- 高德定位服务,插件清单里带的,本地打包需手动搬过来 -->
<service android:name="com.amap.api.location.APSService" />
...
</application>
④ 本地打包时 config.json 的 maven 依赖可以留空
{ "dependencies": [], "abis": ["arm64-v8a"], "minSdkVersion": 23 }
同一份工程以后若改用云打包,记得把 maven 坐标补回去(见 11.2)。
⑤ 打包运行流程
- HBuilderX:发行 → 原生 App-本地打包 → 生成本地打包 App 资源;
- 把生成的资源覆盖到
<AS工程>/simpleDemo/src/main/assets/apps/<appid>/www; - Android Studio Rebuild → 安装到手机;
- HBuilderX:运行到手机 → 运行基座选自定义调试基座。
11.4 页面写法(两种打包通用)
<!-- 必须 nvue 页面;必须给明确宽高,不支持 flex:1 -->
<lj-amap-nav v-if="naviReady" ref="navi" style="width:750rpx;height:560rpx"
@location="onLocation" @log="onLog"></lj-amap-nav>
onReady() {
this.naviReady = true // 等页面渲染完再挂载组件
}
要点:
- Key 优先写进插件
utssdk/app-android/index.vue的const API_KEY = '你的Key'(日志apiKey 来源=builtin); - 用
:apikey="变量"也可以(必须全小写),nvue 下不保证生效,见「十二」; - 用
v-if延迟到onReady再挂载,避免初始化时属性还没到; - Key 在组件创建时一次性生效,改了要重新进页面;
- 不要在页面里自己调
plus.android.requestPermissions申请定位,用组件自带方法; - 坐标必须是 gcj02。
11.5 自检清单
正常日志(按顺序出现):
[lj-amap-nav] [life] NVLoad 开始
[lj-amap-nav] [life] apiKey 来源=builtin
[lj-amap-nav] [life] 地图初始化成功
[lj-amap-nav] [life] 导航实例创建成功
[lj-amap-nav] [view] 导航视图加载完成
崩溃日志位置:<项目>/unpackage/logs/android/<时间>_<包名>_crash.log
| 现象 | 原因 | 处理 |
|---|---|---|
NoClassDefFoundError: Lcom/amap/api/maps/MapsInitializer |
高德类没进 dex(多半是跑在标准基座 / 旧基座) | 云打包:补 maven 依赖并重打基座;本地打包:jar 放 libs、so 放 jniLibs、assets 合并 |
Duplicate class com.amap.api.* |
maven 依赖与手工放进 libs 的 jar 同时进包 |
二选一:删 libs 里的 jar,或清空 dependencies(插件本身不再携带 jar) |
WARNING 未传入 apiKey |
插件 API_KEY 与属性都为空 |
把 Key 写进 API_KEY;若走属性,检查是否全小写 |
地图黑屏 / 算路 code=7 |
Key 与「包名 + SHA1」(iOS 为 BundleId)不匹配 | 高德后台核对签名;换基座后 SHA1 会变,Key 要同步更新 |
code=13 |
Key 非法或为空 | 同 WARNING 未传入 apiKey |
code=-2 / code=12 |
没有定位权限 | 系统设置里允许定位(含「精确位置」) |
code=19/20 |
骑行 / 步行起终点太远 | 组件已自动就近,属正常提示 |
| 地图空白但不崩 | 缺 so 或 assets | 检查 jniLibs/arm64-v8a 的 7 个 so、assets/ 是否已合并 |
十二、Key 取不到(WARNING 未传入 apiKey / code=13)的排查
12.1 现象
页面已经写了 :apikey="amapKey",日志却是:
[lj-amap-nav] [life] NVLoad 开始
[lj-amap-nav] [life] WARNING 未传入 apiKey:请在页面用 :apikey 传入你的高德 Key(插件内不含 Key)
[lj-amap-nav] [life] 高德 Key 与隐私合规已设置
[lj-amap-nav] [life] 地图初始化成功
[lj-amap-nav] [life] 导航实例创建成功
...
[lj-amap-nav] [navi] ERROR 算路失败 code=13(驾车) 算路失败,key非法或过期
定位能成功([loc] 定位成功 ...),但一算路就 code=13。
12.2 原因:nvue 里属性的注入时机晚于组件初始化
Weex 创建组件
└─ 创建原生视图 → 组件 NVLoad() ← 此刻读 apikey,仍是默认空串
└─ 把 :apikey 写进组件属性 ← 属性到这一步才注入
└─ NVLoaded / NVLayouted
而 Key 在 NVLoad 阶段就被用掉了(高德要求设置 Key 早于创建导航实例),之后再补设也不生效,最终算路拿到空 Key → code=13。
12.3 为什么这些写法都没用
| 写法 | 为什么无效 |
|---|---|
apikey="你的Key"(静态属性) |
同样是「组件创建之后」才注入 |
:apikey="常量" / :apikey="computed" |
与取值方式无关,注入时机不变 |
:apikey + :api-key 双写 |
两个属性都还没注入 |
v-if 等 onReady 再创建组件 |
只改变「何时创建组件」,不改变「创建与注入的先后」 |
12.4 解法(三种,按省事程度排序)
A. 把 Key 写进插件常量(推荐,最稳)
// utssdk/app-android/index.vue 顶部
const API_KEY = '你的高德Key'
日志会变成 apiKey 来源=builtin。对外发布的插件包不要把真实 Key 提交上去,发布版保持 '',由使用者自己填。
B. 交给高德 SDK 读清单
在 AndroidManifest.xml 里加:
<meta-data android:name="com.amap.api.v2.apikey" android:value="你的高德Key" />
注意插件里要「有 Key 才设置」,否则空串会把清单里的 Key 覆盖掉。
C. 页面属性传 + 延迟挂载组件
v-if 等到 onReady 再创建组件,同时用全小写 :apikey。这是对使用者最省事的写法,但仍不如 A 稳。
12.5 自检
| 日志 | 含义 |
|---|---|
apiKey 来源=builtin |
用的是插件常量 API_KEY(推荐路径) |
apiKey 来源=apikey |
用的是页面属性(nvue 下不保证能拿到) |
WARNING 未传入 apiKey |
两者都为空 → Key 为空,算路必然 code=13 |
十三、常见问题 FAQ
Q1:组件区域空白,只有页面背景色?
- 是否给了明确
height(flex:1无效); - 是否用的自定义基座;
- 页面是不是 nvue。
Q2:一进页面就闪退 / NoClassDefFoundError: com.amap.api...?
高德类没进包。云打包检查 config.json 的 maven 依赖并重打基座;本地打包检查 jar / so / assets。也确认不是跑在标准基座上。详见「11.5」。
Q3:地图黑屏 / 一点导航就失败(code=7)?
Key 与平台身份不匹配:Android 检查「包名 + SHA1」,iOS 检查 BundleId。换基座后 SHA1 会变,Key 要同步更新。
Q4:算路一直 code=13 key非法或过期?
Key 没生效(多半为空)。看日志有没有 WARNING 未传入 apiKey,按「十二」处理。
Q5:点了定位一直停在「正在定位」?
- 看面板/日志里的
err; - 系统定位服务是否打开;
- 室内可能长时间拿不到坐标,可到窗边或改用持续定位等几秒;
- 权限被拒绝的话,去系统设置手动开启。
Q6:首次安装点「一键导航」没反应? 弹系统授权框时点「允许」即可,插件会自动继续,不需要再点一次。点了「拒绝」才需要去系统设置开启。
Q7:骑行 / 步行点了没反应?
多半是终点太远(code=19/20)。插件会自动就近,日志里有提示;也可直接把终点设在起点附近。
Q8:退出页面后语音还在播?
onUnload 里调用 stopRoute()(插件在页面销毁时也会自动停止)。如果拦截了返回(onBackPress 返回 true),页面没真正销毁,就不会触发清理。
Q9:切换导航方式后导航没了?
正在导航时切换会先停止当前导航,切换后需要重新调用 startRoute*()。
Q10:@location 收到了但拿不到参数?
两端读取位置不同:Android 在 e.detail,iOS 在事件对象顶层,按 7.1 的写法兼容。
Q11:改了 Key / 改了插件代码,为什么没生效?
- 只改 Key(常量或属性)→ 重新进页面即可;
- 改了
uni_modules/lj-amap-nav/下任何文件 → 必须重打自定义基座。
十四、版本记录
v1.0.1(2026-10-19)
- 文档合并为单一 README:原独立的
打包注意事项.md、changelog.md内容全部并入本文(不再随插件分发多个文档) - 补充「本地打包 / 云打包」下 Key 配置的差异(原 changelog 条目):云打包会自动合并插件自带的
AndroidManifest.xml(含高德 Key 的meta-data与定位服务声明),三种 Key 方式都可用;本地(离线)打包不会自动合并,若采用「清单方式」配 Key,必须把meta-data手写进 AS 工程的src/main/AndroidManifest.xml,否则表现为地图黑屏 /code=7 - 新增「十一、打包注意事项(云打包 / 本地打包)」:maven 依赖、
libsjar 说明、离线工程 so / assets 解压脚本、build.gradle、清单合并、自检清单 - 新增「十二、Key 取不到的排查」:
WARNING 未传入 apiKey/code=13的现象、原因、无效写法与三种解法 - Key 配置增加「写进插件常量
API_KEY」路径(日志apiKey 来源=builtin),与属性方式并存,三种方式任选 - Android 依赖改为纯 Maven 拉取:
config.json只保留合包坐标com.amap.api:navi-3dmap-location-search:11.2.100_3dmap11.2.100_loc11.2.100_sea9.8.1,并删除插件内 95.8 MB 的 jar- 修正过一处无效坐标:
com.amap.api:navi-3dmap在 Maven Central 最高只有10.0.800_3dmap10.0.800,没有 11.x,写navi-3dmap:11.2.100_...会Could not find构建失败 - 收益:插件体积 96 MB → 几十 KB;APK 里不再有
assets/uni_modules/.../libs/*.jar这份 94.6 MB 的冗余(实测 debug APK 180.6 MB → ≈86 MB) - 离线打包改为两种方式:AS 工程用 gradle 依赖(推荐)或手工准备 jar / so / assets,详见「11.3」
- 修正过一处无效坐标:
v1.0.0(首个发布版本 · 2026-10-19)
- 三种导航方式:驾车 / 骑行 / 步行,同一组件内切换(Android / iOS 双端)
- 半屏嵌入:按
style指定尺寸,可与业务 UI 同屏 - 一键导航、模拟导航、内置语音播报
- 骑行 / 步行自动就近(骑行 20 km / 步行 5 km)
- 单次定位
locateStart()、持续定位startContinuousLocation(intervalMs) - 事件回传:
@location(坐标 / 错误码 / 回调次数)、@log(运行日志) - 首次安装自动申请定位权限,授权后自动继续
- 页面销毁自动停止导航、停止语音、释放定位
- 已知限制:Android 仅
arm64-v8a、minSdkVersion 23;必须使用自定义基座;iOS 不支持后台定位
十五、许可与声明
- 导航、地图、定位能力由高德开放平台提供,使用时须遵守《高德地图开放平台隐私权政策》及高德相关服务条款;
- 使用前请自行在高德开放平台申请 Key,Key 的配额与安全由申请者负责。

收藏人数:
下载插件并导入HBuilder
赞赏(0)
下载 20
赞赏 0
下载 12694417
赞赏 1956
赞赏
京公网安备:11010802035340号