Push V3:来自服务器的一条消息即可触达所有界面
[LOADING...]
推送通知应作为应用基础设施,而不是一堆不断过期的证书和厂商专属 JSON。
本周,我们将 Push V3 合并到了 Codename One 核心中,并完成了其新的云端实现。它为应用提供了类型化消息模型、托管式厂商凭据、订阅、服务端分组、活动、分析,以及直达 Surfaces 的路径。
还有一件事,所有现有推送开发者现在都应该做:
将推送服务 URL 从
https://push.codenameone.com改为https://cloud.codenameone.com,并通过你现有的代码发送一条真实通知。
下周,我们计划下线旧推送服务,并将 push.codenameone.com 流量直接指向新实现。兼容端点接受现有请求格式,所以现有代码应该能继续工作。但这仍然是一台全新的服务器,“应该”并不等于测试结果。请在切换前测试,当前两条路由还可以轻松对比。
TL;DR
推送是今天的头条内容。在接下来的四篇文章中,我会详细拆解本次发布的其他变化:
- 我们自己的 Maven 仓库:正在将 Codename One 发布迁移到
repo.codenameone.com。下周起,新项目会自动获得该配置。现有项目需要添加一小段 POM 配置,以免新版本不再出现在 Maven Central 上。 - 设备端 AI 与 MCP:核心现在内置了可移植的 OCR、视觉、语言和 LiteRT API。MCP 服务器可以通过回环地址在移动端上检查并操作真实应用,并带有发布构建保护,因为其他本地进程也能访问该套接字。
- 健康数据:新 API 覆盖 HealthKit、Health Connect、锻炼、营养、八种蓝牙健康传感器配置文件,以及一个确定性模拟器。该设计保留被拒绝的读取、缺失值、来源重叠和合规边界,而不是把它们折叠成方便的答案。
- 循路地图路线:
Routing.showRoute(...)现在可以把两个坐标转换为道路几何、距离、时长、路段和步骤。OSRM 提供默认测试路径,而RouteService将生产环境中的提供商选择保留在应用代码中。
立即测试新的推送服务器
如果你的服务器当前使用传统端点发送,请保持请求完全不变,只修改主机名:
在你应用所支持的每个平台上发送到测试设备。测试可见通知、数据负载、冷启动,以及你使用的任何角标、声音、类别、图片或深链行为。将结果与旧主机进行对比,如果两者不一致,请提交 issue。
新服务器包含一个传统兼容层。现有应用不需要在切换主机名之前采用 Java V3 客户端或新的 REST API。这种分离很重要:验证新传输是一个很小的运维变更,而采用 V3 模型则是可以自行安排计划的应用变更。
队列会记录每个目标的厂商响应。“已接受”意味着 APNs、FCM 或其他厂商接受了请求。但它并不能证明操作系统显示了通知、用户看到了通知,或者应用被打开。控制台将这些状态分开,因为一个定义错误但令人安心的数字,比没有数字更糟糕。
V3 让消息成为真正的类型
传统 API 将行为编码为数字推送类型和位置字符串。它能工作,但越来越难以表达厂商差异和新的目的地。
V3 使用不可变模式:
同一个信封可以携带可见内容、应用数据、图片、深链、折叠和生命周期规则、厂商专属选项,以及一个 surface 命令。传入消息在到达应用代码之前就会被解析,通过不可变映射暴露,并且在模式不受支持时被拒绝。
V3 还取代了旧的 PushCallback 契约。你的主应用类不再实现推送接口。你创建 PushClient 并为其提供一个 PushListener:
这不仅仅是方法重命名。监听器会收到已解析的 PushMessage,注册会返回订阅对象,错误会携带代码和重试信息。
在 init() 中创建一个客户端,保留它,并在 start() 中调用 register()。注册是幂等的。不要在 stop() 中注销,因为这会移除订阅,而不是暂停订阅。
运行自有推送基础设施的应用不会被托管服务限制。PushTransport 是用于自定义注册和投递的公开接口,而 PushRegistrationSink 允许应用将注册变更同步到自己的后端。
一条推送可更新通知或 Surface
锁屏通知只是目的地之一。桌面小组件、实时活动、灵动岛、手表复杂功能,以及其他 Surfaces 也需要最新状态。
V3 在同一个信封中保留了一个类型化的 surface 对象。原生引导代码可以在主应用 UI 运行之前路由 Surface 命令。
[LOADING...]
考虑一个配送应用,当顾客等待快递员时,应用并没有在运行。服务端推送可以更新其实时活动和灵动岛,显示“快递员还有 2 分钟到达”,而无需启动主 Codename One UI。在没有该 Surface 的平台上,同一活动可以投递普通通知作为替代。
消息视图仍会显示 APNs 或其他厂商是否接受了每次更新。当应用内部没有运行 PushListener 而 Surface 发生变化时,这一点很有用。
证书不再是你的服务器的问题
旧方案通常要求应用团队生成推送证书,将其放在自己的服务器上,跟踪过期日期,然后不断重复。这看起来是配置,其实是脆弱的基础设施。
新控制台会为每个应用和环境存储厂商凭据。APNs 可以使用 .p8 签名密钥,它没有旧证书工作流的年度过期周期。推送服务会为厂商请求签名,并将凭据与活动用户隔离开。
[LOADING...]
凭据在静态时加密,并在控制台中作为只写机密处理。读取应用设置不会返回机密值。这确实消除了应用服务器上的证书托管,但并不能免除常规的机密卫生习惯:使用范围狭窄的厂商密钥,在团队成员或系统边界变化时轮换密钥,并区分生产环境和开发环境。
无需将身份交给设备即可进行分组
控制台将应用、环境、订阅、受众、消息、活动和分析分开。
[LOADING...]
设备可以通过公共客户端端点注册其厂商令牌。但它不能声明外部用户身份,也不能给自己附加任意标签。这些操作需要经过认证的服务器 API。否则,被修改过的客户端可以直接给自己打上 premium、administrator 或 patient-high-risk 的标签,进入本不属于它的分组。
[LOADING...]
已保存的分组会在服务器端根据应用作用域的订阅数据进行评估。一个分组可以选择语言区域、应用版本、平台,或者由你的后端分配的标签。受众在消息发送时解析,因此修正标签后无需重建静态邮件列表。
这是针对应用行为的分组,而不是广告画像。Codename One 不会出售订阅数据,也不会跨客户合并数据。该服务仍然必须保留投递所需的数据:厂商令牌、安装标识和可选的外部标识符、服务器分配的标签、消息负载、目标状态和厂商响应。
切勿将密码、访问令牌、医疗结果或其他机密放入通知负载。厂商和操作系统都参与投递,锁屏可能会显示可见文本,而通知数据也可能比你预期展示它的界面存活更久。
回答运维问题的监控
新的消息视图会暴露排队中、已接受、失败和无效目标,包括厂商错误信息。
[LOADING...]
这支持多种运维检查:
- 队列是否在推进?
- 是否有一个厂商失败,而其他厂商接受了消息?
- 过期的设备令牌是否正在被移除?
- 速率限制是否延迟了大型受众?
- 这条消息来自哪个环境和活动?
分析数据保留 30 天。它们是投递运维分析,而不是注意力证明。应用打开次数或业务结果,仍应留在由你控制的、符合知情同意的产品分析中。
各套餐包含什么
推送发送和管理式厂商凭据在包括 Free 在内的所有订阅级别均可用。各套餐的区别在于每月数量、速率限制和持久化活动工具:
Free 和 Basic 应用可以通过同一条持久化厂商管道发送。Pro 增加保存的模板、分组、活动和分析。Enterprise 增加自动化和更高的运维限制。配额是面向组织和席位的,因此团队可以查看一次通知运行消耗了哪项额度。
这些数字只是初始策略,而不是在说每个应用都需要一百万次通知。从小而明确的受众开始。一条帮助到 200 人的精准通知,好过一场让 20 万人学会关闭通知的模糊轰炸。
Maven 仓库迁移:第一阶段
我们还合并了从 Maven Central 迁移到 Cloudflare R2 上 Codename One 仓库的第一阶段。
Maven Central 完全有权设置商业使用限制并收取基础设施费用。Codename One 也有一种很难塞进这些限制的工作负载。一次发布目前会发布大量重复的 fat-jar 内容,导致我们的仪表盘数据显示:存储占用 2.12 GB,而软性指南约为 80 MB;文件数 19,962,而指南为 1,000;发布数 27,而指南为 7。
[LOADING...]
这些限制是软性指南,仪表盘同时提供开源调整方案和商业计划。我们离开不是因为 Sonatype 做错了什么。我们离开是因为我们每周、多平台的发布形态在那里托管成本太高,而我们可以在更适配的基础设施上免费为用户提供相同的 Maven 布局。
第一阶段将实测发布负载从 229.5 MB 减少到 76.9 MB。发布管道现在会把已签名的 Central 暂存树复制到 R2,因此不会执行可能导致字节不一致的第二次构建。
[LOADING...]
现有项目可以通过在两个 Maven 解析路径中添加该仓库,为切换后的版本做好准备:
第二个配置块很重要。Maven 会单独解析构建插件和普通依赖。如果只添加 块,未来某个 ``codenameone-maven-plugin 版本可能无法被发现。
新项目会在 8 月 7 日自动获得该配置。现有版本仍保留在 Central 上。新仓库保证至少六个月的留存历史,而优化后的负载按当前发布频率大约可以容纳 1.6 年。
R2 对象存储和 Cloudflare 边缘网络应能改善依赖解析,并消除 Central 对我们的发布路径造成的节流。我们还期望 CI 和发布变得更快速、更稳定。这些是我们在双发布期间将要度量的预期,而不是已经验证的结果。
Maven 文章将于 8 月 4 日发布,包含完整负载审计、切换计划、R2 发布保障、留存策略,以及我们在双发布期间测试的失败模式。## 设备端 AI 与 MCP:覆盖每个平台
PR #5467 将视觉、语言和 LiteRT 推理引入核心。该 API 覆盖 OCR、条码识别、人脸检测、图像标签、姿态检测、自拍分割、文档矫正、语言识别、翻译、智能回复,以及应用自有的 .tflite 模型。
公开 API 保持可移植,但实际处理仍在设备端完成。Android 在较高层级操作中使用 ML Kit。Apple 移植版在适用处使用 Vision、Core Image 和 Natural Language。不支持的平台会报告“不支持该能力”,而不是悄悄把输入上传到云端后备方案。
OCR 特意采用异步设计,因为原生转换和识别不能阻塞 Codename One EDT:
推理会话会在多次运行之间保持应用自有的模型处于加载状态。运行时模型下载可以使用 ModelCache;它要求 HTTPS,并在激活文件前校验 SHA-256 摘要。模型会改变应用行为,因此接受未经验证的模型字节,等同于制造软件供应链漏洞。
PR #5472 将现有的语义 MCP(Model Context Protocol)服务端扩展到 JavaSE 之外。在支持回环连接的平台上,大语言模型(LLM)可以读取组件树、按语义标识找到按钮、设置文本、触发操作,并在实际应用中检查结果状态。
[LOADING...]
这用应用语义取代了坐标猜测。同时,它也创建了一条控制通道。绑定到 127.0.0.1 可以防止意外暴露到局域网,但不会对设备或工作站上的其他进程进行身份验证。
默认情况下,MCP 拒绝在发布构建中启动。虽然存在显式覆盖开关,可用于受控测试实验室,但不应让它成为消费级应用中的便利开关。
关于 AI 与 MCP 的文章将于 8 月 2 日发布,内容包括能力矩阵、可移植推理模型、语义调试闭环,以及回环仍需要发布构建门禁的原因。
健康数据:拒绝虚假的确定性
PR #5475 为 HealthKit、Health Connect、已记录锻炼、稀疏营养数据、确定性模拟,以及八种已采用的蓝牙健康传感器配置文件,新增了一流的 API 支持。
公共 API 按应用需要理解的边界进行划分:
模拟器、桌面和 JavaScript 构建会返回 LOCAL_ONLY。这是一个支持读写的受支持存储,而不是缺少提供程序。只有 Android 提供程序失败时,才应引导用户进入提供程序设置:
[LOADING...]
有些 API 看起来比较保守,是因为平台契约本身就保守。HealthKit 不会透露用户是否拒绝了读取权限。授权页完成只表示系统询问过用户,并不代表应用可以读取该类别。因此,公共 API 没有 hasReadPermission() 方法,因为在 iOS 上它会给出不实信息。
该 API 在整个模型中保留了这些区别。空聚合返回 null,而不是 0。按日历日分桶需要时区。手机和手表数据源保持可区分,因为静默添加重叠样本可能会让一次步行被重复计数。不支持的手机映射会以 TYPE_NOT_SUPPORTED 失败,而不是返回一个看似成功的空集合。
模拟器包含一种模式:在不报错的情况下允许写入但拒绝读取。这让你可以测试宽松假实现会漏掉的 UI 错误:当唯一准确的说法是“没有可用数据”时,却指责用户拒绝了访问权限。
这个 API 不会让应用自动符合 HIPAA 合规要求。它从不上传健康数据,可以强制校验特定用途字符串,并拒绝不支持的写入。访问控制、加密、审计记录、留存、用户同意、泄露处理、后端契约和应用商店披露,仍然需要应用自行负责。
关于健康数据的文章将于 8 月 1 日发布,内容包括平台矩阵、授权陷阱、样本模型、变更游标、锻炼、蓝牙传感器、模拟器故障模式、构建配置和 HIPAA 边界。
折线不是路线
地图折线只是连接已经存在的坐标。它无法发现坐标之间的道路、行驶时间、转向动作或替代路径。
PR #5480 新增了 com.codename1.maps.routing。最小可用路径是两个坐标:
该调用会立即返回。活动的 RouteService 找到路线后,API 会绘制路线几何形状,并在 Codename One EDT 上调整地图取景范围。
[LOADING...]
默认服务是 OSRM,因此首次请求驾车路线不需要注册提供方。它指向公共 OSRM 演示服务器,该服务器没有生产级服务等级协议(SLA),并且使用汽车配置。WALKING 请求并不会把这张路网图变成步行路线。
生产应用可以将 OsrmRouteService 指向自己的服务器,或安装其他提供方:
应用使用可移植的 Route 对象,而不是提供方返回的 JSON。这些对象包含备选路线、距离、时长、边界、几何形状、路段、分步指引、转向位置和提供方元数据。
这是路线规划,而不是逐向导航。该版本没有声称支持重新规划路线、交通预测、离线地图包或语音引导。这些功能需要位置更新、生命周期状态和提供方特定规则。
关于路线规划的文章将于 8 月 3 日发布,内容包括自定义路线样式、预计到达时间(ETA)处理、编码折线支持、OSRM 限制、出行方式、提供方替换,以及路线结果与导航之间的边界。
明天,我会从新的 Health API 开始,逐一讲解这些改动。今天,请大家通过 cloud.codenameone.com 发送一条真实通知。这一周,你可以并排比较旧的和新的推送实现。如果任何行为有差异,请在我们下周切换流量之前提交 issue。
本文《Push V3:从你的服务器到每个平台的一条消息》首发于 foojay。