周五,Codename One 的开发团队在博客里放出一篇文章,宣告这个跨平台框架正式把手表开发纳入了版图。文章不长,却把 watchOS 和 Wear OS 两种完全不同的可穿戴系统一并讲清楚了——重点不是教程,而是那一小段让两边都能跑的 Java 或 Kotlin 代码,到底怎么写、怎么跑通的。
不少开发者对 Codename One 还比较陌生。它是个开源框架,只用一个 Java 或 Kotlin 的代码库,就能编译出原生的 iOS、Android、桌面和 Web 应用。也就是说,你写一份业务逻辑,它帮你生成为每个平台的真正 native 部件,而不是套一个壳。这次更新的手表支持,也延续了同样的思路:复用已有的手机端代码,只针对手表屏幕做有限的适配,而不是从零重写一个表盘应用。
为什么会有人在乎一个手表 API?苹果的立场其实很明确,它把手表编程当作与手机、桌面完全割裂的一门手艺——不同的 API,不同的交互逻辑,连最基础的 UI 隐喻都没法复刻。比如,你在 iPhone 上随手就能调出的文本输入框,在 watchOS 里压根不存在;网页浏览控件就更别想了。所以,当很多知名应用干脆放弃手表界面时,一个很自然的问题就冒了出来:在这样的限制下,费劲搞一个手表 API,图什么?
Codename One 团队给出的答案是:复用。别看适配一只手表屏幕只需要不多的界面改动,但因为要学一整套陌生的 API,真动手做起来成本并不低,于是大量 app 直接选择“没有手表 UI”。而 Codename One 的做法是,继续让你用同一份 Java 或 Kotlin 代码库干活,手机端怎么驱动界面,手表端就怎么驱动,只是按屏幕尺寸和交互特性,由你自己决定每一屏到底展示哪些内容。工作量被压缩在一小块条件判断和布局调整上,而不是整套重写。
如果说理念层面是对复用价值的坚持,那么 watchOS 这一端的工程实现就更有意思了。苹果自家的 watchOS 完全看不到 UIKit 的视图层级,也没有 OpenGL ES,更没有 Metal——iOS 移植所依赖的所有渲染路径,在这里一概走不通。所以,Codename One 的 watchOS 版本专门做一个基于 Core Graphics 的渲染后端,再另起一个手表应用 target,把整个运行时和被 ParparVM 翻译过的应用代码,塞进一个 SwiftUI 的壳里运行。无论是矩形、线条、多边形、图片、渐变,还是 Core Text 绘制的字符串,以及裁剪、变换、alpha 遮罩等操作,所有绘图指令最终都交由 Core Graphics 来画。这么一来,手表屏幕上出现的并不是一套简化的“假”组件,而是完整的 Codename One UI 绘制结果。
团队还特意贴出了一张来自测试框架的截图。那个测试框架原本根本不是为手表设计的,界面上竟然还带有一个文本输入框——因为它是 Codename One 原生的文本组件,所以照样能在手表上正常渲染,甚至能“看起来能用”,直到你真想在上面打字,才会意识到手表上根本不该出现这玩意。这件事恰好提醒开发者:组件虽然能渲染出来,不代表它适合出现在手表上。真正为手表构建的界面,会主动收起文本输入框,改用旋转表冠或点击来获取输入。本质上,你还是需要为手表重新设计每一屏的体验,而框架只是保证你的代码无需为两个平台各写一份。
在苹果这边,手表 App 的起点是一个自动生成的 SwiftUI @main 壳,它负责承载 Codename One 的每一帧,并把旋转表冠和触摸事件转发给框架内部的运行时。编译到真机上时,手表这部分 slice 的目标架构是 arm64_32。如果你选用默认的捆绑发行方式,这个手表 App 会嵌入到你的 iOS 主应用里,用户在 iPhone 上一装,手表端就同步装好;如果你想单独发布,也可以直接构建一个纯手表产品。架构上,团队给出的示意图很清晰:CN.isWatch() 这个判断在苹果手表和安卓 Wear OS 上都会返回 true,所以无论哪个平台,你首先会因为屏幕尺寸而要做界面适配;一旦过了这个检查,接下来的差异就只来自平台本身,而不是这个条件分支。
最后,官方还特别提了一句:单独维护一个手表端的根结构,目的是让应用体积足够小。手表的运存和 CPU 预算远低于手机,能少跑一行代码,就少跑一行。所以,业务逻辑层面依旧共享同一份代码,但最终打进手表包里的,只有跟你筛选出来的手表视图强相关的部分。这种“隔离根”的设计,让复用不是嘴上说说,而是真正控制了手表的资源开销。
热门跟贴