本文基于 HarmonyOS 6.1 的「扫码直达」官方文档与 App Linking 接入指导整理。文中代码是为说明问题自写的完整示例,不是官方示例的搬运;API 名称、枚举取值与版本号等事实性信息均标注官方出处;涉及真机表现的部分已明确标注,未做任何实测数据编造。
本文基于 HarmonyOS 6.1 的「扫码直达」官方文档与 App Linking 接入指导整理。文中代码是为说明问题自写的完整示例,不是官方示例的搬运;API 名称、枚举取值与版本号等事实性信息均标注官方出处;涉及真机表现的部分已明确标注,未做任何实测数据编造。
有个做本地生活的朋友跟V哥抱怨:他在应用里生成了一张"扫一扫点单"的码,发出去给用户。用户用系统的扫一扫一拍——应用是打开了,但停在了启动页。点单页在哪?用户得自己再点两下。
这事V哥第一反应是"是不是V哥没配对"。翻完官方文档才看明白: 从 6.1 起,系统把'扫码直达'这件事接好了大半,但'扫完落在哪一页'这件事,系统不知道,得你告诉它。
换句话说,入口是系统给的,落地是应用自己的活。把这两件分清楚,整套能力就顺了。
官方的描述很直白:开发者把域名注册到"扫码直达"服务后,用户通过 控制中心等系统级常驻入口 ,扫描应用的二维码、条形码,就能跳转到应用对应服务页,实现一步直达( 接入"扫码直达"服务 )。
V哥把整条链路拆成四步,画成一张业务流:
| 步骤 | 谁来做 | 你写不写代码 | | --- | --- | --- | | ① 域名注册到扫码直达服务 | 你在 AGC + 开发者网站 | 不写,配 | | ② 用户从系统扫码入口发起扫码 | 系统(控制中心等) | 不写 | | ③ 系统解析码值、查到对应应用 | 系统 | 不写 | | ④ 拉起应用、跳到对应服务页 | 系统拉起 + 你做路由 | 写 |
一眼看清了吧: 前③步都是系统的事,只有第④步的"落到哪个页",需要你接住码值再路由。
这里有个硬边界先说在前:扫码直达 仅支持中国境内 (香港特别行政区、澳门特别行政区、中国台湾除外)接入使用( 扫码直达 · 说明 )。如果你的应用做海外发行,这条链路在你那不生效,别硬等。
扫码直达到底靠什么把"码"和"你的应用"绑在一起?答案是 App Linking 。
官方的开发准备写得很明确:针对扫码直达, App Linking 是必选项 。你需要做四件事( 开发准备 · 仅针对扫码直达必选 ):
在 AGC 控制台开通 App Linking 服务;
在开发者网站上关联应用;
在 App Linking 中配置二维码、条形码关联的网址域名;
在应用的 module.json5 里关联这个域名。
只能用 HTTPS 网址 。App Linking 当前仅支持 HTTPS,HTTP 域名直接不被接受。
不能用自动签名 。官方明确:接入 App Linking 不能使用 DevEco 的自动签名功能,必须手动签名。第一次V哥卡在这半天,编译能过、扫码不跳,最后发现是签名方式不对。
应用未安装时,码值对应的网页也得准备好 。用户手机没装你的应用,系统会拉起浏览器打开那个 HTTPS 页面——这是你"沉默获客"的兜底。
V哥的判断就一句: 域名是门票,App Linking 是那条门缝。门缝没开,后面再怎么写路由都是空转。
顺带说一句为什么这件事值得做:扫码直达的入口是系统级的常驻入口(控制中心等),比你在应用里自己塞一个扫码按钮浅得多。用户不用先打开你的 App、再找扫码入口,而是"系统一拍、直接落到服务页"。对拉新和复访来说,这条路径省掉的就是那两下点击——而这恰恰是最容易流失的地方。所以域名这一步,别嫌配起来麻烦。
系统拉起应用时,码值(那个 HTTPS 链接)会落在 Want 的 uri 字段里。你要做的,是在 EntryAbility 的两个入口都接住它: 冷启动走 onCreate ,热启动走 onNewWant 。
下面是自写的完整示例,把接住码值、再跳页的逻辑收在 routeByUri 一处:
import
{
UIAbility
,
Want
}
from
'@kit.AbilityKit'
;
import
{
BusinessError
}
from
'@kit.BasicServicesKit'
;
import
{ hilog }
from
'@kit.PerformanceAnalysisKit'
;
import
{ router,
window
}
from
'@kit.ArkUI'
;
export
default
class
EntryAbility
extends
UIAbility
{
private
uiContext?:
UIContext
;
onCreate
(
want
:
Want
):
void
{
this
.
routeByUri
(want);
}
onNewWant
(
want
:
Want
):
void
{
this
.
routeByUri
(want);
}
onWindowStageCreate
(
windowStage
:
window
.
WindowStage
):
void
{
windowStage.
loadContent
(
'pages/Index'
);
windowStage.
getMainWindow
().
then
(
(
win:
window
.Window
) =>
{
this
.
uiContext
= win.
getUIContext
();
});
}
private
routeByUri
(
want
:
Want
):
void
{
const
uri
:
string
|
undefined
= want?.
uri
;
if
(!uri) {
return
;
}
const
target
:
string
=
ScanRouter
.
match
(uri);
if
(
this
.
uiContext
) {
const
state
: router.
RouterState
=
this
.
uiContext
.
getRouter
().
getState
();
if
(state && state.
name
!==
'Access'
) {
this
.
uiContext
.
getRouter
().
pushUrl
({
url
: target }).
catch
(
(
err: BusinessError
) =>
{
hilog.
error
(
0x0001
,
'[ScanDirect]'
,
`路由失败 code:
${err.code}
`
);
});
}
}
}
}冷、热启动都接 :只写 onCreate ,应用本来就在后台时再扫一次码,你收不到第二次——因为走的是 onNewWant 。这个坑很隐蔽,不报错,只是"第二次扫码没反应"。
uiContext 异步拿到 : getMainWindow() 是 Promise,页面没加载完时 uiContext 还是空的,直接路由会空指针。教训是路由动作要等 uiContext 就绪。
已经在目标页就别压栈 :不加这个判断,用户连扫两次,回退键要按好几下才能出去。
接住 uri 之后,最忌讳的就是"在 ability 里写一堆 if-else 判断路径"。码一多,逻辑就糊了。
V哥的做法是把"路径前缀 → 目标页"抽成一张规则表,集中维护:
import
{ hilog }
from
'@kit.PerformanceAnalysisKit'
;
import
{ url }
from
'@kit.ArkTS'
;
interface
RouteRule
{
pattern
:
string
;
page
:
string
;
desc
:
string
;
}
export
class
ScanRouter
{
private
static
rules
:
RouteRule
[] = [
{
pattern
:
'/scan/pay'
,
page
:
'pages/Pay'
,
desc
:
'扫支付码 → 支付页'
},
{
pattern
:
'/scan/order'
,
page
:
'pages/OrderDetail'
,
desc
:
'扫订单码 → 订单详情'
},
{
pattern
:
'/scan/coupon'
,
page
:
'pages/Coupon'
,
desc
:
'扫券码 → 领券页'
},
];
static
match
(
uri
:
string
):
string
{
try
{
const
path
:
string
=
new
url.
URL
(uri).
pathname
;
for
(
const
rule
of
ScanRouter
.
rules
) {
if
(path.
startsWith
(rule.
pattern
)) {
return
rule.
page
;
}
}
}
catch
(err) {
hilog.
error
(
0x0001
,
'[ScanDirect]'
,
`URI 解析异常:
${err}
`
);
}
return
'pages/Index'
;
}
}把路由收口成表,好处是: 新增一种码,只改这张表,不动 ability。 这是工程上最划算的一笔。
写完代码,别急着上线。官方给了两条验收标准,V哥建议你逐条对着查( 开发后验证 ):
| 标准 | 类型 | 你在验证什么 | | --- | --- | --- | | 应用已安装跳转体验 | 规则(必须满足) | 扫支付码跳到 支付页 ,不是首页 | | 应用未安装跳转体验 | 建议 | 系统拉起浏览器,打开码值对应的网页 |
第一条是硬指标。V哥那个朋友的问题,恰恰就是扫完停在首页——他压根没做路由,URI 到了 Want 里就被忽略了。
V哥的铁律:履约页不是首页。 扫支付码,用户要的是支付,不是你的启动页。断在首页,就是断在体验上。
第二条是兜底。没装应用的用户,扫了你的码,应该被引到网页完成动作或下载——这条做不好,等于白白浪费一次触达。
扫码直达本身依赖系统路由,应用侧改动不大。但 6.1 在 Scan Kit 上确实加了料,和你做扫码相关功能时能用上( Scan Kit 开发详解 ):
默认界面扫码标题动态适配 : ScanOptions 里把 scanTypes 限定成单一制式(如只 QR_CODE ),系统扫码页标题会自动变成"扫描二维码"或"扫描条形码"。
Wearable 全支持 :6.1 起,带后置相机的穿戴设备也能用默认/自定义界面扫码。
复杂场景算法加固 :曲面码、小角度码、污损码、远距离小码识别率提升。
但要把话说清楚: 这些是 Scan Kit 的增强,不是扫码直达的增强。 扫码直达的核心是"系统扫、应用落"的路由分发,应用侧那三步(注册域名、接住码值、路由履约页)在 6.1 的主线上没变。别把两件事混成一团去讲。
还有一个边界提醒:扫码直达要求中国境内,且 必须真机验证 ——模拟器不支持相机相关扫码能力,图像识码部分可模拟器调试,完整链路必须以真机实测为准( Scan Kit · 支持设备 )。
把上面几点整理成一份可以贴进 PR 描述的清单:
域名已在 AGC 开通 App Linking,并配置到扫码直达服务了吗?
module.json5 里关联了正确的域名吗?
用的是 HTTPS 域名吗?(HTTP 不被接受)
用的是 手动签名 吗?(自动签名会导致扫码不跳)
EntryAbility 的 onCreate 和 onNewWant 都 接住了 want.uri 吗?
路由动作等 uiContext 就绪后才执行吗?
码值 → 页面的映射收口在一张路由表里了吗?还是散落的 if-else?
扫支付码 / 订单码,跳的是 履约页 而不是首页吗?
应用未安装时,码值对应的网页能正常打开吗?
连续扫码会不会重复压栈(已在目标页不再 push)?
中国境内以外发行的版本,有降级方案吗(此能力不生效)?
在 真机 上用控制中心扫码入口完整走通一遍了吗?
本文涉及的事实性信息(API 名称、枚举取值、版本号、官方约束)来自以下官方文档,文中的结构、代码示例、决策流程与自检清单为本人整理编写:
接入"扫码直达"服务
Scan Kit(统一扫码服务)· 开发准备
Scan Kit(统一扫码服务)开发详解
App Linking 接入指导
统一扫码服务(ArkTS)Codelab
最后一句 :扫码直达真正难的不是代码——满打满算就三步。难的是 把"系统扫、应用落"这件事分清楚 :门票(域名)交给 App Linking,接住码值写进 EntryAbility,路由履约页收成一张表。前三件做对,用户一扫就到;少一件,用户就停在首页。
转载声明:本文为转载整理,原文版权归原作者及原站点所有。
原文来源:Juejin HarmonyOS
原文作者:威哥爱编程;如有侵权,请联系管理员删除。