背景:通过微信打开一个网页,点击后跳转到地图 App(百度地图等)进行导航。核心难点在于微信内置浏览器对第三方 App 唤起(URL Scheme)的限制。

一、要解决什么问题

  • 用户从微信里打开一个链接,最终要能在手机地图 App 里进入导航。
  • 有现成 POI 数据(campus_pois.json,613 个校园及周边点,坐标是百度 bd09ll 坐标系)。
  • 目标地图 App 先用百度地图,后续要扩展高德、腾讯地图。

二、几套方案与结论

方案 A:百度地图 URI API(Web 端跳转)

  • 官方文档 https://api.map.baidu.com/lbsapi/cloud/uri.htm,v2.0,免费、免 AK。
  • 服务地址形如:

    • 标点 api.map.baidu.com/marker
    • 地址解析 api.map.baidu.com/geocoder
    • POI 检索 api.map.baidu.com/place/search
    • POI 详情 api.map.baidu.com/place/detail
    • 路线规划 api.map.baidu.com/direction
  • 通用必填参数:output=html(Web 端必填)、src=webapp.companyName.appName(统计必选)。
  • 坐标系:默认 bd09ll。现有数据恰好是百度搜索接口返回的 bd09ll,无需转换直接落点。
  • 实测:这些接口 302 重定向到 map.baidu.com,在普通浏览器里打开正常。
  • 局限:跳转到的是「百度地图网页版」,不是直接唤起 App。

方案 B:自己建落地页 + URL Scheme 自动唤起

  • 思路:详情页展示 POI 信息(图片、介绍、详情),点击后进入跳转页,跳转页用 baidumap://map/direction?... 唤起百度地图 App。
  • 结论:在真浏览器(Safari/Chrome)里 window.location = 'baidumap://...' 能直接唤起;但在微信内置浏览器里,第三方 scheme 会被静默拦截,无法自动唤起。
  • 落地页思路本身成立,但微信场景下不能自动唤起。

方案 C:微信开放标签 wx-open-launch-app(官方微信内跳 App 能力)

  • 官方文档 https://developers.weixin.qq.com/doc/oplatform/Mobile_App/WeChat_H5_Launch_APP.html。
  • 硬性条件:已认证服务号 + 已认证开放平台账号(同主体)+ 网页域名绑定为服务号 JS 接口安全域名 + 域名与移动应用绑定。
  • 只能跳转自己审核通过的 App,不能唤起百度地图(第三方 App)。
  • 结论:不适用于「跳百度地图」这个目标。

方案 D:应用宝微下载(microdown.yyb.qq.com)—— 最终采用

  • 百度地图 URI 在微信内点击"打开地图"时,走的正是应用宝这条链路。
  • 用手机 UA(微信内核)+ 无头浏览器实测,抓到了完整机制。

D 的核心机制(已实证)

应用宝页面不直接跳 baidumap://,而是通过微信注入的私有 JSBridge 方法完成,完整调用序列:

1. WeixinJSBridge.invoke("getInstallState", {packageName: "com.tencent.android.qqdownloader"})  // 查应用宝
2. WeixinJSBridge.invoke("getInstallState", {packageName: "com.baidu.BaiduMap"})               // 查百度地图
3. WeixinJSBridge.invoke("launch3rdApp", {})                                                    // 试探权限
4. WeixinJSBridge.invoke("getInstallState", {packageName: "com.tencent.mm"})                   // 查微信
5. WeixinJSBridge.invoke("launchApplication", { schemeUrl: "tmast://appdemo?...applink=<base64>" })

第 5 步的 schemeUrl 关键字段:

tmast://appdemo?photonid=...&params=scene:40027, via:ANDROIDWXZ.YYB.WX.OPEN,
  pkgName:com.baidu.BaiduMap, appName:百度地图,
  applink:<base64>, autoOpen:true

applink 解码后就是 baidumap://map/direction?mode=driving。

本质:tmast://appdemo 唤起应用宝 App,应用宝再执行 applink 里的 baidumap:// 唤起百度地图。这是「中转」而非直接唤起。

三条子路径

场景微信内动作走的 JSBridge 方法
已装百度地图唤起 tmast:// 中转 → baidumap://launchApplication
已装、需跳浏览器"离开微信"→系统浏览器→baidumap://openUrlByExtBrowser
未装微信安全下载 APKdownload3rdApp + appdown.md.qq.com

关键结论(真机实测确认)

  • 这些 JSBridge 方法(launchApplication、openUrlByExtBrowser、getInstallState)只在微信内置浏览器里、且仅对腾讯白名单域名(a.app.qq.com / microdown.yyb.qq.com / appdown.md.qq.com 等)注入。
  • 真机实测:在普通域名页面上,WeixinJSBridge 上根本不存在 openUrlByExtBrowser 等方法,只有基础的公开方法。第三方域名无法调用这些私有方法。
  • 因此,「自己做一个类似应用宝的落地页来唤起」不可行——不是技术门槛,是微信服务端白名单。

三、最终确定的妥协

微信内跳地图,统一走应用宝微下载直连(microdown.yyb.qq.com/o/simple.jsp)。

  • 不自己写后端做 scheme 唤起(微信私有 JSBridge 白名单问题,做不了)。
  • 也不经 api.map.baidu.com → map.baidu.com 中转,而是直接跳应用宝落地页,链路最短:微信 → microdown.yyb.qq.com → tmast:// 中转 → baidumap://
  • 正确拼法(关键:整个 scheme 必须整体 URL 编码):
https://microdown.yyb.qq.com/o/simple.jsp
  ?pkgname=com.baidu.BaiduMap
  &android_schema=<整个 baidumap scheme 整体 URL 编码>
  &src=webapp.demo.campus

其中 android_schema 的值是一整个 URL 编码后的 scheme:

baidumap://map/direction?mode=driving&destination=name:终点|latlng:29.822838,121.568971&coord_type=bd09ll
  • 不传起点:baidumap:// 里不带 origin,由百度地图自行定位当前起点,减少一步输入。
  • 编码坑(已实测):destination(及 origin)必须作为 scheme 的一部分整体编码塞进 android_schema。若把它们写成 microdown 页面的独立参数(用 & 分开),microdown 不读取,最终 applink 会丢失起终点。实测:整体编码 → applink 解码含完整 destination;独立参数 → applink 只剩 mode=driving。
  • iOS 差异:android_schema 只对安卓生效;iOS 需要用 ios_schema 参数单独拼。
  • 坐标体系:baidumap:// 用 bd09ll,现有数据正好是 bd09ll,零转换;扩展高德/腾讯时换 pkgname(com.autonavi.minimap)和 scheme 前缀(amapuri:///iosamap:///qqmap://),并把 bd09 → gcj02。

四、可复用的经验

  1. 微信内置浏览器对第三方 URL Scheme 静默拦截,任何普通网页都无法直接 location = 'baidumap://' 唤起。
  2. 微信「离开微信、在浏览器打开」提示,不是网页 JS 触发的,是微信对白名单域名(腾讯系)的系统级能力。
  3. 应用宝微下载的本质是「tmast:// 中转 scheme + 微信私有 JSBridge」,整套只对腾讯域名开放。
  4. 微信公开 JSSDK 里没有「跳转外部浏览器」或「唤起任意第三方 App」的接口。
  5. 百度地图 URI API v2.0 仍有效、免 AK;output=html 和 src 在 Web 端必填;默认 bd09ll 坐标。
  6. 网络实测环境注意:本机 7890 代理端口抓百度地图接口返回 502,7897 端口可用;urllib 默认 UA 会被识别为爬虫返回 403,建议用 curl + 浏览器 UA 或无头浏览器。

五、补充:URL Scheme 双协议头 + WebView 拦截(2026-08 依据官方 uri/api 文档确认)

场景:网页/WebView 里点"百度地图"按钮调起 App 导航。上文方案 B 只确认了"真浏览器能唤起、微信被拦",本节补齐协议头本身就该分两套这一层。

5.1 native 与 web 用不同协议头

百度官方 uri/api 文档(实现原理一节)明确,Android 与 iOS 一致:

场景scheme由谁处理
native(第三方 App 原生 Intent / WebView 壳内)baidumap://壳 shouldOverrideUrlLoading 拦截 → ACTION_VIEW Intent
web(浏览器 / 网页应用)bdapp://系统直接路由,不经过壳

网页应用调起 iOS/Android 百度地图路线规划(官方示例格式):

bdapp://map/direction?origin=纬度,经度&destination=纬度,经度&coord_type=bd09ll&mode=walking&src=webapp.companyName.appName
  • origin/destination:裸坐标(先纬度后经度)或 latlng:纬度,经度;iOS 端不认 Android 的 latlng:..|name:.. 组合段,只有名称时也不带 name:。
  • coord_type:默认 bd09ll(本项目数据正好是 bd09ll,显式传更稳)。
  • src:必填,ios/andr/webapp.companyName.appName,不传不保证服务。
  • mode:支持 walking/driving/transit/riding 等,walking 可直接用。
  • Android native(壳内)路径 baidumap:// 则认 latlng:..|name:.. 组合格式(Android 文档示例 origin=latlng:34.26,108.95|name:我的位置)。

5.2 WebView 壳必须拦截 scheme,否则跳报错页

WebView 默认不把自定义 scheme 交给系统——壳不拦,点外部地图就跳"网页无法打开"报错页(本项目实测到的现象)。拦截方式是 WebViewClient 覆写,不是 JS 桥(JS 桥 addJavascriptInterface 是给网页调原生能力用的,与调起第三方 App 无关):

  • Android:shouldOverrideUrlLoading 拦下 scheme → startActivity(Intent(ACTION_VIEW, uri)),调起前 resolveActivity 判空(未装不 start,否则抛 ActivityNotFoundException)。
  • iOS:WKWebView decidePolicyFor 拦下 → UIApplication.open;App 内探测 scheme 还要 Info.plist 加 LSApplicationQueriesSchemes(这才是 iOS 的"白名单",网页没有白名单概念)。

浏览器(Safari/Chrome)里不需要任何代码,系统直接路由 scheme;报错页只说明 App 没装 / scheme 没注册 / 协议头不对。

5.3 项目落地方案(校园导航)

前端导航页按"有无原生桥"自动二选一,而不是按 iOS/Android 分:

target = getNativeBridge() ? nativeSchema : webSchema
// 有 CampusNative 桥(壳)= baidumap:// + latlng:..|name:..(native)
// 无桥(浏览器,iOS 与 Android 通用)= bdapp:// + 裸坐标 + coord_type=bd09ll(web)

学校桥接包 android/compass-bridge-for-school/ExternalMapLaunch.java:静态 handleExternalUrl(view, request, context),学校把它并进已有 WebViewClient 的 shouldOverrideUrlLoading 第一行即可,不覆盖其原有 https 放行逻辑。拦截名单:baidumap/bdapp/amapuri/qqmap/geo + https 降级域名(map.baidu.com / api.map.baidu.com / uri.amap.com)。调起结果回推 window.CampusNavigation.receiveExternalLaunch(success, scheme) 供前端 toast。

5.4 待真机验证 / 已知不可靠项

  • 新版(2024-2026)iOS/Android 百度地图是否仍注册 bdapp://:官方文档如此规定,但新版 App 行为未实测;若失效可交叉试 baidumap:// 头。
  • 网页版导航降级链接实测(2026-08):

    • api.map.baidu.com/direction?destination=..&mode=walking&region=..&output=html&src=webapp.xx.xx → 200 可用,302 到 http://map.baidu.com/mobile/?third_party=uri_api#place/linesearch/..(http 非 https)。
    • 三个坑:① 重定向到 http;② mode 参数会丢,用户到页面还要自己选步行/驾车/公交;③ 终点名称传递错乱(页面 word 恒显示"终点",坐标正确但名称不对)。
    • map.baidu.com/direction(不带 output=html)404、map.baidu.com/?qt=s 403 反爬——必须走 api.map.baidu.com/direction?...&output=html 这套 v2.0 web URI。
    • 定位:这是"打开百度移动网页版路线页",不是唤起 App,无实时步行导航/语音引导。只能当微信内"能看路线、不报错"的兜底。
  • iOS 微信内走应用宝 microdown 基本不可用(应用宝 iOS 版已停运),微信 iOS 场景需另想办法。

5.5 教训

  • iosSchema = androidSchema 直接复用 Android 格式 → iOS 端失效。命名 androidSchema/iosSchema 本身就是误导,真实维度是 native vs web,应命名 nativeSchema/webSchema。

六、补充:微信 iOS 侧——microdown 为什么死 + 微信 iOS 跳第三方 App 机制(2026-08)

6.1 microdown 是纯 Android 链路,iOS 无中转

上文方案 D 的完整调用序列依赖两个 Android 专有环节:

microdown 页 → WeixinJSBridge.launchApplication({schemeUrl:"tmast://appdemo?applink=<base64 的 baidumap://>"})
            → 唤起应用宝 App → 应用宝执行 baidumap:// → 百度地图
  • tmast:// scheme 是应用宝 App 注册的协议头。应用宝 iOS 版早已停运(2016 前后下架,苹果不允许第三方应用商店),iOS 上没有任何 App 认领 tmast://,中转第一步就断。
  • launchApplication 这套私有 JSBridge 是 Android 微信对腾讯白名单域名注入的,iOS 微信没有这套 tmast 中转方法。

结论:microdown.yyb.qq.com 在 iOS 上最多显示一个下载引导页,ios_schema 参数无人执行,死链。

6.2 iOS 微信跳第三方 App 的机制(与 Android 本质不同)

iOS 微信内核是 WKWebView。网页 location.href = "baidumap://" 触发 WKWebView 的 decidePolicyForNavigationAction,放不放行由微信(宿主 App)决定——微信在 WebView 层就拦截了,iOS 系统的 scheme 确认框都到不了。

方式iOS 微信内原因
microdown 应用宝中转✗应用宝 iOS 版下架,无 tmast:// 可中转
location.href = bdapp://(URL Scheme)✗(基本被拦)微信 WKWebView 拦截第三方 scheme(2023-12 起明显收紧,社区实测:点击无反应或提示"在浏览器中打开")
Universal Link(iOS 9+ 标准深度链接)✗微信会"劫持" Universal Link:即使目标 App 配置了 apple-app-site-association,微信里也把它当普通网页打开,除非该 App 在微信开放平台关联并通过校验
wx-open-launch-app 开放标签仅限"自己的 App"微信官方唯一认可路线,但要求已认证服务号/开放平台、且只能唤起自己审核通过的 App,无法唤起第三方百度地图

6.3 iOS 微信跳百度地图结论 + 兜底

iOS 微信里直接唤起百度地图 App,目前没有可靠技术路线。 兜底两条:

  1. 引导用户"右上角 ··· → 在浏览器中打开",浏览器里再跳 bdapp://(浏览器路径可用)。
  2. 降级成"复制终点名/坐标",让用户到百度地图里自己搜。

6.4 对项目 navi 页的建议

navigateExternal 现在微信场景(不论 iOS/Android)都走 microdown。建议:

  • 检测到 iOS + 微信 → 不走 microdown,改为 toast 提示"请右上角'在浏览器中打开'后使用外部导航"(或直接复制坐标)。
  • Android 微信的 microdown 链路也建议真机复测(依赖应用宝是否安装 + 微信白名单域名是否仍放行)。

相关笔记


本文归档于个人知识库 · 领域:CS & AI · 模块:04 Project_CS_AI/Web