AbilityResult 返回结果:页面选择、扫码、授权回调如何统一处理
AbilityResult 返回结果:页面选择、扫码、授权回调如何统一处理
页面选择图片、扫码、授权登录这些场景都需要从另一个 Ability 返回结果。如果调用方各写各的回调,结果码、取消状态、参数字段和异常提示很快会不一致。 这类问题通常不是某个 API 写错,而是工程边界没有提前定好。本文从 ResultRequest 这条主线出发,把场景判断、协议设计、代码封装、异常兜底和验证方法串成一套可落地方案。

本文围绕 扫码取值、选择地址、授权登录、文件选择 展开,重点解决四个问题:
- 这类能力应该放在入口、页面、服务层还是扩展能力里。
- 协议字段怎样设计,后续排查才不会靠猜。
- 代码如何封装,才能避免每个页面各写一套。
- 异常、重试、日志和验证用例应该怎样补齐。


1. 先明确适用场景和边界
实际项目里,扫码取值、选择地址、授权登录、文件选择 经常横跨多个模块。如果边界不清,调用方会把临时字段塞进参数里,接收方也只能被动兼容。更稳的做法是先明确“谁发起、谁处理、失败谁兜底”。
| 判断点 | 推荐做法 | 不建议做法 |
|---|---|---|
| 调用来源 | 明确 source 或 owner | 只看当前页面路径 |
| 业务目标 | 用 scene 或 type 表达 | 直接传内部实现名 |
| 异常处理 | 启动前校验,接收方兜底 | 等页面空白后再排查 |
| 日志字段 | 从入口生成 traceId | 出问题后再补日志 |
2. 先定义稳定协议模型
协议模型要比页面实现更稳定。页面可以改名,Ability 可以迁移,但 ResultRequest 这类入口模型不要频繁变。
export interface ResultRequest {
requestCode: string;
scene: string;
resultCode: string;
payload: string;
traceId: string;
}
export interface ResultRequestResult {
ok: boolean;
reason?: string;
}
代码解释:
ResultRequest把扫码取值、选择地址、授权登录、文件选择需要的最小字段收敛到一个结构里。- 字段不是为了“传得多”,而是为了启动、接收和日志三端都能对齐。
- 后续扩展字段可以新增,但核心字段不要随意改名。
3. 字段设计要面向排查
很多线上问题不是无法修,而是无法定位。下面这些字段看起来简单,但能把调用来源、业务目标和链路日志串起来。
| 字段 | 类型 | 工程作用 |
|---|---|---|
requestCode |
协议关键字段 | 启动、解析、日志都围绕它对齐 |
scene |
协议关键字段 | 启动、解析、日志都围绕它对齐 |
resultCode |
协议关键字段 | 启动、解析、日志都围绕它对齐 |
payload |
协议关键字段 | 启动、解析、日志都围绕它对齐 |
traceId |
协议关键字段 | 启动、解析、日志都围绕它对齐 |
4. 启动前先做校验
不要把所有异常都留给接收方。启动前能发现的问题,应该在封装层直接拦住,并给出可记录的原因。
export class AbilityResultCoordinator {
static validate(input: ResultRequest): ResultRequestResult {
if (!input.requestCode) return { ok: false, reason: 'requestCode is empty' };
if (!input.scene) return { ok: false, reason: 'scene is empty' };
if (!input.resultCode) return { ok: false, reason: 'resultCode is empty' };
return { ok: true };
}
}
代码解释:
AbilityResultCoordinator是协议入口的第一道门。- 空字段、非法来源、缺少 traceId 这类问题越早拦截越好。
- 返回结构带
reason,页面可以提示,日志也能直接记录。
5. 构造系统参数要集中到工厂
如果每个页面都自己拼系统参数,字段名迟早会分裂。把转换逻辑收敛到 ResultWantFactory,调用方只提交业务模型。
import { Want } from '@kit.AbilityKit';
export class ResultWantFactory {
static create(input: ResultRequest): Want {
return {
abilityName: 'ResultPayloadParser',
parameters: {
...input,
generatedAt: String(Date.now())
}
};
}
}
代码解释:
- 工厂只负责从业务协议转换到系统入口参数。
generatedAt这类辅助字段统一补,避免调用方各自处理。- 以后目标 Ability 或参数结构变化,只改工厂和路由表。
6. 接收方必须再次解析和兜底
启动前校验不能替代接收方校验。来自通知、卡片、外部入口、系统恢复的参数都有可能缺失或过期。
export class ResultPayloadParserHandler {
async handle(input: ResultRequest): Promise<void> {
const result = AbilityResultCoordinator.validate(input);
if (!result.ok) {
StageIssueStore.save('20', result.reason ?? 'unknown');
return;
}
StageTraceLogger.info(input.traceId, '扫码取值、选择地址、授权登录、文件选择');
}
}
代码解释:
ResultPayloadParserHandler不相信外部输入,先走同一套校验。- 失败时写入
StageIssueStore,页面可以展示错误态,而不是直接白屏。 - 成功后记录 trace 日志,方便跨生命周期排查。
7. 生命周期里要处理重复进入
Stage 模型下,入口可能来自冷启动,也可能来自已有实例的重复拉起。只在首次创建时处理参数,后面就容易丢入口。
export class Stage20EntryBridge {
private latest?: ResultRequest;
update(input: ResultRequest, lifecycle: 'onCreate' | 'onNewWant' | 'onForeground'): void {
const result = AbilityResultCoordinator.validate(input);
if (!result.ok) {
StageIssueStore.save('20', result.reason ?? 'invalid input');
return;
}
this.latest = input;
StageTraceLogger.info(input.traceId, lifecycle);
}
current(): ResultRequest | undefined {
return this.latest;
}
}
代码解释:
update同时支持onCreate、onNewWant和前台恢复。- 最新入口保存在桥接层,页面只读取解析后的安全数据。
- 生命周期名进入日志,能判断问题发生在哪个阶段。
8. 失败补偿要有明确策略
不是所有失败都应该重试,也不是所有失败都要提示用户。可以按错误来源做分层:参数错误直接兜底,系统约束延后处理,业务失败进入可重试队列。
export enum Stage20FailureAction {
ShowFallback = 'show_fallback',
RetryLater = 'retry_later',
Ignore = 'ignore'
}
export function resolveStage20Failure(reason: string): Stage20FailureAction {
if (reason.includes('empty') || reason.includes('unknown')) {
return Stage20FailureAction.ShowFallback;
}
if (reason.includes('network') || reason.includes('timeout')) {
return Stage20FailureAction.RetryLater;
}
return Stage20FailureAction.Ignore;
}
代码解释:
- 参数类错误通常没有重试价值,应该展示兜底。
- 网络、超时、系统约束类问题可以进入延迟重试。
- 失败策略写成函数,后续能加日志、埋点和实验开关。
9. 验证用例要覆盖正常和异常
只验证正常路径没有意义。下面这些用例能覆盖 扫码取值、选择地址、授权登录、文件选择 的核心边界。
export const stage20CheckCases = [
{ name: '用户取消', expect: '可复现并有明确结果' },
{ name: '扫码成功', expect: '可复现并有明确结果' },
{ name: '授权拒绝', expect: '可复现并有明确结果' },
{ name: 'payload 缺字段', expect: '可复现并有明确结果' },
{ name: '重复 requestCode', expect: '可复现并有明确结果' }
];
代码解释:
- 用例名称直接对应真实问题,方便测试和产品一起确认。
- 每个用例都要有明确期望,不能只写“能打开”。
- 如果后续改协议,这组用例就是回归清单。
10. 常见问题排查表
| 现象 | 高概率原因 | 处理方式 |
|---|---|---|
| 返回后页面没更新 | requestCode 没对应上 | 统一分配 requestCode |
| 取消也当成功 | 未判断 resultCode | 解析器先判断状态 |
| 授权失败无提示 | 错误分支被吞掉 | 补取消和失败态 |
| 结果字段不一致 | 目标 Ability 各自定义字段 | 统一 ResultPayload |
11. 小结
AbilityResult 返回结果 的关键不是把代码写到能跑,而是把边界提前设计清楚:入口模型要稳定,启动前要校验,接收方要兜底,生命周期要覆盖重复进入,日志要能串起整条链路。这样文章里的方案迁移到真实 Stage 工程后,才不会随着入口变多而失控。
更多推荐
所有评论(0)