通知权限拿到了发布还是失败
发布时间:2026/8/14 17:22:28 作者:尧图编辑部 阅读量:1,286

“通知权限已经开了为什么发布还是失败”这句话听上去像在问通知服务实际很多时候是在问页面状态。第一次做这个流程时我把文件准备、通知授权和发布结果塞进一个笼统的“成功/失败”。页面一旦失败大家都盯着权限页面一旦成功大家就默认铃声已经能听到。两边都不准确。后来我把状态拆开才看清权限 granted 只表示系统通知开关允许文件准备完成表示 sound 有了候选输入publish returned 表示服务接受了本次请求。三个阶段有先后关系但没有一个可以替另一个盖章。一个总状态为什么特别容易误导如果页面只有isSuccess它会面临一个尴尬的问题。文件写入失败时该是 false权限拒绝时也是 falsepublish 抛错时仍然是 false。用户看到 false根本不知道下一步要重试复制文件、去系统打开通知还是检查请求参数。现有页面没有把这些压成一个布尔值。它保留了文件、权限、发布、错误、最近操作和触发时间State soundState: NotificationSoundState { sandboxPath: 尚未准备, permissionState: unknown, publishState: idle, errorMessage: 暂无错误 }; State fileState: string 等待准备 EL1 文件; State lastOperation: string 尚未操作; State lastTriggerTime: string 尚未触发;这一组状态像一张很小的工单。它不只告诉我“失败了”还告诉我失败发生在哪个阶段、页面最后做了什么、这件事是什么时候发生的。对于会穿过文件系统、权限服务和通知服务的流程来说这种分开记录比一个漂亮的成功图标实用得多。我第一次查错是因为把授权当成终点当requestNotificationPermission()返回 granted 后我以为最难的一段已经过去便直接去看声音。可是发布函数还有一段明确的前置校验if (this.soundState.sandboxPath 尚未准备 || !this.isValidSound(this.soundValue)) { this.updatePublish(failed, 发布前校验失败请先成功准备 EL1 文件和 uri:: sound。); this.markOperation(发布前校验失败); return; }也就是说权限已经给了发布仍然可以因为文件没准备好而被正确地拦下。这个 failed 不是通知服务拒绝也不是权限突然失效它只是页面不允许一个不完整的请求继续往下走。否是否是否是准备 EL1 文件文件与 sound URI 是否合格publishState: failed错误: 先准备文件和 uri:: sound核对通知是否启用permissionState 是否 grantedpublishState: failed错误: 通知权限未授予调用 notificationManager.publishPromise 是否返回publishState: failed publish 错误publishState: published以前我看见 failed 会立刻打开系统设置后来才学会先读错误文本。它会告诉我“发布前校验失败”“通知权限未授予”还是“publish 失败”。同样是红色状态走回去的路完全不一样。串行流程为什么比并排点按钮更适合排错页面提供了单独按钮也提供runTrackedFlow()。后者把准备文件、核对权限、发布固定成顺序执行中间任何一步不满足就停止。private async runTrackedFlow(): Promisevoid { this.autoRunState 串行流程运行中; await this.prepareSandboxSound(); if (this.soundState.publishState failed) { this.autoRunState 串行流程停止文件准备失败; return; } await this.requestNotificationPermission(); if (this.soundState.permissionState ! granted) { this.autoRunState 串行流程停止通知未授权; return; } await this.publishNotification(); }我喜欢这个写法是因为它不让失败后的页面继续假装“正在努力”。文件失败就停在文件失败权限拒绝就停在权限拒绝。每个 return 都是一个清楚的分界线在这之前已完成什么在这之后没有发生什么。如果把三个按钮随意并排点排查记录很容易交错。你可能在文件还没写完时就触发发布也可能在系统授权弹窗没处理完时再次发请求。最终状态看起来像随机的实际只是操作顺序不受控。串行流程不保证所有系统问题消失但至少把页面自己的顺序固定了。状态拆分后的调用过程NotificationManager系统通知开关EL1 文件页面NotificationManager系统通知开关EL1 文件页面alt[通知未启用][通知启用]alt[文件或 URI 不合格][文件合格]prepareSandboxSoundfailedautoRunState 停在文件准备失败readyisNotificationEnableddeniedautoRunState 停在通知未授权grantedpublish(request)resolved 或 error更新 publishState 与 lastOperation这里的lastOperation和lastTriggerTime也不能省。一个状态值如果脱离时间很难判断它是刚才这次操作留下的还是页面自动流程第一次运行时的旧结果。尤其页面aboutToAppear()会调度一次自动验证手动再次点击时更需要知道眼前看到的是哪一轮。我会这样测试这页测试不是只看最终published。我会人为观察每个停止点文件准备失败、通知未授权、发布前校验失败、publish 返回。每个分支都有不同的页面文字不能混成一张“失败截图”。否是否是否是重新进入页面或记录自动流程状态fileState 是否完成检查 sourceSize targetSize soundValue 错误permissionState 是否 granted检查系统通知开关与申请结果执行 publishNotificationpublishState 是否 published读取 errorMessage 和 lastOperation记录 publish Promise 已返回分别在系统界面确认可见性人工确认自定义声音是否实际播放最后两步不是摆设。published表示 Promise 成功返回不能替代系统通知栏是否显示更不能替代人在设备上是否听到预期铃声。把这两层分开遇到“通知有了但没声音”时就不会回头怀疑文件写入遇到“声音设置没问题但通知被系统静默”时也不会拿 URI 格式背锅。失败后的复测也要回到起点我不会在失败页面上只点一次发布再判断修好了。先记录fileState、permissionState、publishState、lastOperation和errorMessage然后从准备文件重新走。若文件阶段已经失败后面的授权和发布都不应被当作本轮结果若权限状态不是 granted则 publish 分支会在调用服务前停止。只有三个前置字段都符合预期published才能说明这次notificationManager.publish的 Promise 已返回。页面有固定通知 ID也会在进入时调度一次自动流程因此时间字段特别重要。人工点按钮前先看最近操作能避免把页面首次进入时留下的状态当成刚才的结果。手动再跑一轮后我会比较最近操作是否依次变成文件准备、授权核对、publish 返回或某个明确的停止原因这种比较针对的是页面流程是否按顺序更新不能借此替代系统通知是否可见或声音是否播放的确认。这次问题让我留下的习惯我把每一次 failed 都先翻译成人话publishStatefailed本身没有足够信息。它可能表示文件准备阶段已经失败也可能表示发布前校验拒绝了输入还可能表示通知未启用或者服务调用抛出了错误。页面把具体原因放进errorMessage把最近一步放进lastOperation因此我会先读这两个字段再决定下一步。错误写着准备文件失败时不再申请权限错误写着通知未授予时不把 URI 拿出来反复转换错误来自 publish 时才保留该错误并检查请求条件。这里有一个很容易忽略的后果文件准备失败会同时让发布状态失败但它不表示 publish 被调用过。源码在真正调用通知服务之前已经有输入门禁沙箱路径还是尚未准备、或者 sound 格式不合规就直接 return。写复盘时若把这种 failed 描述成“通知服务发布失败”会把责任放到根本没有执行的调用上。相反published只能对应 Promise 成功返回表示请求被服务接受它也不替代通知栏和人工听觉的确认。权限状态也不是一次写死的标签。每次发布前代码都会再次调用isNotificationEnabled()再把结果写回 permissionState。这个复核动作很重要因为用户可能在两次操作之间修改系统开关页面上旧的 granted 不能自动代表现在。回归时我会先观察发布前刷新后的权限字段再看 publish 分支而不是把先前授权弹窗的结果当成永久事实。这样状态来自本轮查询问题单里的时间也更可靠。自动流程和手动按钮并存时顺序尤其要留意。页面进入后会延迟启动一次串行验证随后用户还可以手动准备、授权、发布。如果只截取最终状态很难知道那是自动运行留下的还是刚才手动点出的。lastTriggerTime和lastOperation用来解决这个问题。我会在手动操作前记下旧值操作后确认时间和操作名称已经变动再解释对应的状态不变就说明本轮动作没有走到预期位置需要先检查触发而不是分析结果。用停止点把回归分成几段第一段只准备文件要求 fileState 完成且 sound 满足格式第二段只核对系统通知开关记录 granted 或 denied第三段才调用 publish 并看 Promise 结果。每一段结束都保存页面字段。某段失败后停止不让下一段制造新的状态覆盖旧错误。下一次重试则从第一段重新开始尤其是文件或 URI 失败后不直接点发布。这个顺序不能保证外部系统一定展示通知却能保证页面没有跳过自己的前置条件。最后我会用两份独立记录收尾。一份是页面运行记录内容是文件、授权、请求和异常另一份是设备观察内容是通知是否可见、何时观察、声音是否由人工确认。两份记录可以互相指向但不能彼此代替。只有这样遇到“请求返回却没有听到声音”时才不会把不属于页面可证明范围的结果误写成发布流程已经失败或成功。为什么固定 ID 也要谨慎解释固定通知 ID 有利于重复测试时识别同一类请求却不能单独证明用户看到了哪一条通知。页面只是把 ID 放进请求不读取系统界面的展示结果。测试记录里我会把它当作关联键同一轮文件准备、权限核对和 publish 请求使用同一个 ID系统界面若要观察也另记观察时间。这样 ID 帮助对齐操作不会被误用成系统可见或声音已播放的凭据。我也会排除“权限已经 granted所以错误一定在通知服务”的猜测。发布函数在服务调用前先检查 sound 输入之后再即时查询通知开关任何一层不满足都会提前结束。因而一张 granted 截图只能回答某次查询的权限结果不能证明当前请求已形成更不能证明 publish 已经执行。真正判断服务调用有没有发生要看最后操作是否写成发布固定 ID 通知以及随后是 Promise 成功返回还是捕获到异常。错误文本最好和本轮时间一起保存。没有时间的failed可能来自页面刚进入时的自动运行也可能来自用户后来的手动点击没有操作名称的 published也可能让人误以为是刚才那次按钮产生的。页面已经给出这两个字段我会在每一段测试结束后记录它们并在下一段开始前确认没有旧状态残留。这个动作看似繁琐实际能避免最常见的误判把上一轮文件失败误读成这一轮授权后的发布失败。如果需要测试拒绝和恢复我会先把系统通知切到未启用运行到明确的停止状态再恢复开关从准备文件重新开始观察 permissionState 是否重新查询为 granted。恢复后也不跳过文件准备因为页面运行的每一轮都应有自己的有效 sound 输入。这样测试覆盖的是状态分支和顺序而不是依赖某次页面残留恰好让发布继续。到最后系统通知是否展示、声音是否能被人工确认仍放在独立观察项中不能由状态机自动写成结论。这套记录方式还有一个好处把页面能够控制的部分和设备外部的部分拆开。前者是准备、检查、请求和错误呈现后者是系统展示和听觉感受。前者可以在页面字段中复查后者需要现场观察。两类事实并列而不是互相替代才能在出现差异时知道应该回看哪一层。我还会在复测结束后检查页面是否留下可解释的终态。文件成功、权限 granted、publish returned 时错误应保持暂无错误最近操作应对应本轮 publish若中途停止自动流程应写明停在文件还是授权而不是继续显示模糊的运行中。这样测试不是只找一个绿色结果也确认失败不会被后续按钮或旧状态覆盖。它让下一次排查从真实停止点重新开始而不是从一个已经失去时间语境的总状态开始。向团队同步时我会把“授权状态正常”和“请求已返回”分成两句话。前一句来自系统开关查询后一句来自 publish 的 Promise两句话可以同时成立也可以只成立其中一句。再把系统可见和听觉观察放到后面独立注明就不会因为一句笼统的成功或失败让文件、权限、服务和设备体验互相背锅。每次记录都应标出这是自动流程还是手动流程。来源明确后状态变化才有可追溯的操作语境也便于发现重复触发带来的干扰。回归时我会先让自动流程结束再手动重新执行一轮并分别记录两轮的最后操作与时间。若手动流程在文件、授权或发布任一点停止就以那一轮的错误文本为准不借用自动流程留下的状态。两轮都显示请求返回也只能说明两次调用都有返回系统界面和人工听觉仍要按各自时间单独观察不能合并成一个成功判断。文件准备、通知授权、发布结果必须分别显示。每次状态变化都要带最近操作和时间避免把旧结果当成新结果。published只能写成“发布请求已被接受”不能写成“用户已经听到铃声”。以前我希望通知页面只给人一个简单答案成功或失败。现在更愿意让它给出一张短路线图。用户走到哪里就看到哪里没有走到的步骤也不要替他涂成绿色。这样页面看上去不那么轻巧出了问题却能很快找到该往回走的那一步。本文依据现有文件准备、授权核对与 publish 状态路径撰写。系统通知可见性和自定义声音的实际播放仍需目标设备上的系统界面与人工听觉单独确认。