S苏哈出行开发者文档

Android 步行导航:从接入到完整行程

本章面向第一次接入的 Android 开发者。目标是完成“取得真实位置 → 选择终点 → 规划步行路线 → 用户确认开始 → 实时引导 → 偏航重算 → 到达 / 退出”的完整流程。配套示例只调用公开接口,不需要修改 SDK。

下载完整步行案例源码 · JavaScript 步行教程 · 导航服务 API

1. 先选对接口

需求 使用接口 是否持续导航
查询步行距离、预计耗时、画预览路线 RouteSearch.calculateWalkRouteAsyn 否
真实步行、转向提示、偏航、到达、录音播报 WalkingNavi 是,前台控制器
驾车 GPS / 模拟导航 NavigationClient / NavigationController 属于驾车流程
自己绘制地图、路线、起终点 MapView、SukhaMap、Polyline、Marker 仅负责显示

WalkingNavi 不会创建 Activity、自动显示地图,也不负责申请权限或持续采集定位。宿主必须提供地图界面和原始定位,处理页面生命周期。不要把驾车的 GPSNaviMode、EmulatorNaviMode、前台服务连接或 startNavi(mode) 套到步行控制器上。

配套 WalkingGuideActivity.java 是可编译的完整前台页面:有地图选终点、定位权限、规划、开始、语音开关、停止、偏航状态、到达和资源释放。优先运行这个页面,再按业务 UI 调整。

2. 五分钟接入准备

  1. 从下载页取得配套 SDK。使用交付包 versions.json 中同批模块和 Maven 仓库,保留 POM 与传递依赖。
  2. 工程至少需要 maps-core、maps-services、maps-location、maps-navigation-services;maps-navigation 按该批 Maven 依赖解析。不要仅复制一个 AAR。
  3. Android Key 的包名、签名 SHA-1、调试 / 发布环境、数据集与实际 APK 一致。初始化成功不等于数据覆盖所有城市;终点应位于该数据集的可步行路网内。
  4. Java 11、minSdk 23;Gradle 和 desugaring 配置可直接沿用交付包 examples/general-android。完整版本见版本与交付。
  5. 将案例里的 WalkingGuideActivity.java 放到 app/src/main/java/example/walking/,在应用 Manifest 注册。无需新增布局 XML。

在 <manifest> 内添加权限,在 <application> 内添加元数据和 Activity:

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

<!-- 以下内容放入 application;用自己的 Android Key 和已授权数据集。 -->
<meta-data android:name="cn.sukha.maps.API_KEY" android:value="YOUR_ANDROID_KEY" />
<meta-data android:name="cn.sukha.maps.SERVICE_URL" android:value="https://map.sukha.cn" />
<meta-data android:name="cn.sukha.maps.DATASET_ID" android:value="YOUR_DATASET_ID" />
<activity android:name="example.walking.WalkingGuideActivity" android:exported="false" />

从自己已有的页面启动:

startActivity(new android.content.Intent(this, example.walking.WalkingGuideActivity.class));

首次进入先点“定位”并授权,等位置有效后在地图点击终点,再点“规划步行”和“开始”。初始地图中心是演示区域,不会被当成真实导航起点。如果已有地点搜索,选择结果后将其公开坐标作为终点即可;不要拿地图屏幕中心代替用户位置。

3. 坐标、时间、精度:最容易出错的三个地方

使用内置地图页面切换驾车 / 步行

内置 SukhaMapFragment 的目的地规划卡片提供“驾车 / 步行”选项。也可在主线程调用:

map.setTravelMode(SukhaMapFragment.TravelMode.WALKING);
map.planRoute(start, end, listener);
// 路线就绪且真实定位有效后,由用户点击开始。
map.startNavigation();
// 切换回驾车会取消旧请求,并重新规划当前起终点。
map.setTravelMode(SukhaMapFragment.TravelMode.DRIVING);

默认驾车以预计时间为首要目标,结合道路等级、通行速度和路口代价优选主路;“距离最短”驾车备选仍遵守车辆通行限制。步行明确请求 DISTANCE,使用步行路网,可走允许通行的步道、小路,不按车辆单行规则绕行,也不会进入禁止步行的高速道路。预计步行时间来自步行算路结果,不复用驾车时间。

内置步行导航复用 WalkingNavi 的前台生命周期。宿主在步行导航期间继续传入真实位置;不能像驾车服务接管定位后那样停止宿主定位。页面退到后台会暂停步行导航,返回后由用户重新开始并校验定位。它不提供后台或锁屏持续步行播报。

WalkingNavi.setVoiceLanguage(language) 可独立设置中文、英文、缅甸语。步行出发、直行、转向、掉头、路线更新、到达及定位不稳使用专用离线录音,与驾车录音区分;切换语言或静音会清理当前播报。旧接口保持可用。

数据 顺序 / 单位 要求
LatLonPoint / LatLng 纬度、经度 例如 new LatLonPoint(21.67623,100.02228)
calculateWalkRoute(start,end) 与 SdkClient 公开坐标模式一致 案例显式使用 WGS84
updateLocation(android.location.Location) 原始 WGS84 即便客户端公开模式为 GCJ02,此入口也不要手工转成 GCJ02
Location.getTime() Unix 毫秒时间戳 保留定位原时间,不用收到回调的时间替换
accuracy 米 必须提供真实精度;未知时不伪造为 0
speed / bearing 米/秒、角度 可缺省;不要把 km/h 当米/秒

示例从 SukhaLocation 转为 Android Location 时保留经纬度、accuracy、time;只有 hasSpeed() / hasBearing() 为真才复制对应值。拿到错误定位时只展示错误,不调用 updateLocation。

4. 推荐状态流与按钮行为

未定位/未选终点 → 定位有效且终点已选 → PLANNING → READY → NAVIGATING
                                  ↓ 失败                ↓ 偏航
                                可重试              SDK 自动重算
                                                      ↓ 到达
                                                   行程已结束
任意状态 → 用户停止 / 页面离开 → 停止定位与导航、清理待处理任务
状态 可以做什么 不应做什么
等待定位 申请权限、重试定位、选终点 用固定点开始真实导航
规划中 显示加载、允许取消 重复开始、连点创建多个控制器
路线就绪 展示距离 / 耗时、确认开始 在定位已过期或起点明显移动后继续使用旧路线
导航中 更新引导、静音、停止 每次定位重新算路、每次回调重复开始导航
偏航重算 展示“正在重新规划” 在重算回调里再次发起同一次算路
到达 展示到达、停止宿主定位 将到达自动等同于业务订单完成

示例将“8 秒内、精度不大于 35 米”的位置作为开始前的保守检查,并在起点移动超过 30 米时要求重算。这是案例的产品策略,不是 SDK 可配置参数或定位精度承诺。开始按钮点击时仍要重新检查,不能只在按钮刚启用时检查一次。

5. 最小调用链

下面代码说明核心顺序;完整权限、按钮、地图和退出逻辑在下载案例里。

SdkClient client = AndroidSdkClient.create(activity, serviceUrl, datasetId,
        "zh-CN", CoordinateMode.System.WGS84);
WalkingNavi walk = new WalkingNavi(activity, client);
walk.setListener(new WalkingNavi.Listener() {
    public void onCalculateRouteSuccess(WalkRouteResult result) {
        WalkPath path = result.getPaths().get(0);
        // 首次规划:绘制 path.getPolyline(),启用“开始”。
        // 导航中的重算也会触发此回调:替换旧线,不再 startNavi()。
    }
    public void onCalculateRouteFailure(SdkException error) {
        // 展示 error.getCode()/getMessage()/getRequestId(),让用户重试。
    }
    public void onNaviInfoUpdate(WalkingProgress.Update u) {
        // 使用 reliable / offRoute / cue / remainingMeters 更新 UI。
    }
});
// 操作一:有真实起点、用户已选择终点之后。
walk.calculateWalkRoute(startPoint, endPoint);
// 操作二:成功回调后,用户点开始;无参数,不传 GPSNaviMode。
walk.startNavi();
// 操作三:导航中,每次有效真实定位回调交给 SDK。
walk.updateLocation(rawWgs84Location);
// 操作四:明确停止。
walk.stopNavi();
// 页面最终释放。
walk.close();
client.close();

上述分阶段语句不能不等异步回调就一次执行。步行控制器没有需要调用的 initialize() 或 onReady;不要从驾车示例复制这两个步骤。所有公开方法在 Android 主线程调用;SDK 内部处理耗时任务,不要在 UI 线程自己做 HTTP 算路。

6. 回调与返回字段逐项说明

WalkingNavi.Listener 回调 触发时机 推荐处理
onCalculateRouteSuccess(WalkRouteResult) 初次规划、自动偏航重算成功 替换路线;用宿主状态区分两种情况
onCalculateRouteFailure(SdkException) 规划或重算失败 展示错误码、说明、requestId;避免无间隔循环重试
onNaviInfoUpdate(WalkingProgress.Update) 导航中的定位处理结果 更新引导状态和剩余米数
onRecalculateRequested() SDK 判断需要重新算路 只展示状态,SDK 已负责请求
onArriveDestination() 满足到达判断 展示到达、停止自己的定位订阅
onVoiceStatus(String) 语音状态反馈 单独显示语音状态,不覆盖整条路线

WalkingProgress.Update 是回调参数的公开类型,无需自行构造进度引擎:

字段 含义与 UI 用法
accepted 此次定位是否被接纳;false 时不要把它显示成可靠的新进度
reliable 本次进度是否可靠;false 时显示定位不稳定,别强行显示下一步转向
offRoute 已偏离路线;不是“恰好不在折线上”的瞬间判断
requestReroute 引擎认为需要重算;上层 WalkingNavi 已接管重算
arrived 到达状态;对业务使用独立的到达事件并处理幂等
progressMeters / remainingMeters 已行进 / 剩余距离,米
deviationMeters 与所匹配路线的偏离距离,米
cue 可能为 null;非空时读取 instruction、id、geometryIndex

步行更新没有驾车的 progress.getInstruction()、remainingSeconds 或车速显示接口。预计总耗时从 WalkPath.getDuration() 读取(秒);如果自己估计动态剩余时间,请明确它是宿主估算。

7. 四个常用业务案例

案例 A:地图地点页“步行去这里”

地点页携带真实地点坐标进入步行页面;页面先取得当前位置,再规划。优先选择适合步行到达的入口,避免以巨大建筑几何中心作为唯一终点。已有业务地点的 ID、标题与终点应绑定保存,用户换地点时停止旧规划并清空旧路线。

案例 B:规划后继续浏览,稍后再开始

保留路线预览,但开始时重新检查定位时间、精度与规划起点。若用户已走远,提示重新规划,不修改旧定位时间来通过检查。示例 plannedOrigin 与 fresh() 演示这个检查。

案例 C:走错路后重新规划

持续向 updateLocation 喂真实位置即可。SDK 触发 onRecalculateRequested 后,UI 显示重算提示;成功后用新 getPolyline() 替换旧线。不要在成功回调中再次 startNavi(),否则会重置进度和语音去重。

案例 D:到达后返回地点页

收到到达事件后显示完成 UI、停止宿主位置采集,保留必要的路线摘要。SDK 会播到达语音;不要在到达回调里立即再 stopNavi(),否则可能截断这段播报。用户真正退出页面时仍要 close()。业务订单是否完成由自己的确认流程决定。

8. 生命周期、语音和网络

完整示例在 onStop 停止本次步行和定位,在 onDestroy 释放 SukhaLocationClient、WalkingNavi、MapView、SdkClient;MapView 的 create/start/resume/pause/stop/save/lowMemory/destroy 均有转发。旋转或页面重建后要求重新规划,不会偷偷恢复一条旧行程。

这是前台步行案例。不要因为驾车 SDK 存在前台服务,就假设 WalkingNavi 自动提供通知栏、锁屏续航或跨 Activity 恢复。确需后台步行时,先设计并验证宿主服务生命周期与目标系统限制;本例不声称已经提供这些能力。

语种随创建时的 SdkClient 语言,支持的语种以当前批次为准;示例使用 zh-CN,可换成 en-US 或 my-MM。setVoiceEnabled(false) 静音,不停止定位或步行进度。不要在每个进度回调自己再次播放提示。

首次规划和偏航重算都需要可用网络及有效授权。已有路线仍在内存时与“支持离线重算”不同;Android 步行控制器没有 JS 的 prepareOfflineNavigation(),不能照抄该方法。

9. 排错表

现象 先检查 修复方向
地图 / 算路授权失败 Key 类型、包名、签名、数据集、错误 requestId 配置匹配实际 APK,检查 debug/release 差异
显示路线但没有导航 是否只用了 RouteSearch 用 WalkingNavi,成功后由用户开始并持续喂定位
开始报 Plan a walking route first 是否等待成功回调 将开始按钮绑定 READY 状态
没有进度 是否 start、是否喂 Location、有无 accuracy、time 是否原始 按案例定位适配器排查
位置一直不可靠 粗略定位、旧缓存、差精度、时间倒退 到开阔处、检查精确位置权限;不要伪造 accuracy
坐标偏移 公开坐标模式与原始定位是否混用 算路用公开模式;Location 一律原始 WGS84
重算后路线叠加 是否每次新增折线而没删除旧线 保存一个 Polyline 引用,替换时 remove
没声音 静音状态、设备媒体音量、语音状态、录音资源 与路线错误分别显示,不重复自播
一到达就截断语音 到达回调是否立刻 stopNavi 停宿主定位即可,用户退出时再 close
切后台停止 是否采用本例的 onStop 策略 属于明确的前台示例行为

10. 开发验收清单

先做接口联调,再做户外真实步行:首次拒绝 / 再授权、无精度定位、旧定位、短路线、连续转弯、实际偏航、重算断网、原地停留、终点附近漂移、修改终点、算路中退出、重复点开始、到达后退出、三种语种、静音恢复、横竖屏切换。不得将人工修改时间戳或坐标后的实验数据当作真实轨迹。

本章完整 Activity 在文档构建时以配套交付 AAR 编译;编译通过证明接口签名匹配,不替代手机定位、扬声器听感和实际路网可达性验收。