无诱饵和转换的开源资助:分析、原生地图、电视及更多功能
[LOADING...]
多年前我写过一篇文章,题为《开源诱饵与换挡》。简而言之:一个项目以开源形式发布,但没有任何商业模式,社区围绕它形成,然后账单到期。项目要么因无人能负担维护费用而沦为废弃软件,要么它悄然推出一种自制的“源代码可用”许可证,默默收回之前赋予的自由。你目睹过这种事发生。我也一样。
我们花了十四年时间,努力不让自己成为这两种故事中的任何一种。这是一篇发布公告,大部分内容是本周上线的新功能,但贯穿这些功能的一条主线值得先说出来,因为本周的两项重点功能——分析功能与上周的崩溃保护——是我们意图在不向你已拥有的自由收费的情况下维持项目运转的最清晰例证。
我们实际提供的契约
Codename One 这个开源项目和 Codename One 这家公司并不是一回事,而这一区别就是全部关键所在。项目必须保持开放:它采用 GNU GPL 协议并附带 Classpath 例外,这与 JDK 所用的协议相同,因此你可以 fork 它,在其上构建闭源应用,并自行运行整个构建工具链。这个协议选择是核心,而非脚注。我在上面链接的那篇文章指出,可持续的开源资助方式是强版权协议加上真正的商业产品,而不是一个宽松的协议,等社区锁定后再悄然重新授权。Codename One 自始至终都是 GPL 加 Classpath 例外。这个例外确保你的应用属于你;而版权协议则确保一个自由、可 fork 的 Codename One 永远存在于世界上,无论公司做什么或谁拥有它。
但公司仍然需要赚到足够的钱,来持续支付推动项目以你今年所见速度前进的人员。这种需求与开放的需求相互拉扯,而公司解决这种紧张关系的常规方式是让开放部分变差,从而使付费部分看起来更优。我们在几周前的《我们不会破坏你的代码》中明确说过:我们不会那样做,下面的模式就是我们避免这种情况的方式。
第一个答案是构建云。在我們的服务器上将你的 Java 编译为原生 iOS 二进制文件需要花费真金白银用于机器和维护,因此对构建容量收费是公平的:你是在为一台有计量表的东西付费。但仅仅靠构建积分无法支持我们一直以来的发布速度,也永远做不到。我们需要第二个能增长的答案。
这第二个答案是可选的服务,它们构建在开源项目之上,增强它,但从不挡你的路。我们坚持的规则是它们保持可选。没有任何东西强迫你使用我们的服务;如果存在替代方案,你可以自由接入它。我们的服务之所以值得选择,是因为集成深度,而非锁定:因为我们运行构建服务器,我们可以使像崩溃保护这样的服务达到一种螺栓式工具无法企及的无缝程度,为你符号化原生崩溃,无需你在客户端进行任何设置。
上周的崩溃保护是这种深度的第一个例子。本周的分析功能则增加了第二种模式:一个位于开放的服务提供者接口后面的付费提供者,因此同样的代码可以像接入我们的后端一样轻松地接入第三方后端。
你可以将分析外观连接到 Google Analytics、Matomo、Firebase 或你自己的 AnalyticsProvider,框架并不关心你选择哪个。如果你选择我们的第一方提供者,你将获得一个注重隐私的分析后端,同意处理已经完成,同时你也为下一个移植、下一个 API、下一个年份提供了资金。这就是全部的提议。它不是对开源项目征税;它是一个更好的默认选项,恰好也同时为开源项目支付了费用。
我们将继续以这种形式增加服务,并且我们欢迎关于想要哪些服务的请求。我们很乐意在下面的评论中听到你的意见。如果这种模式对你来说合理,你能做的最有用的事情就是帮助我们向人们介绍这些服务。推广它们并不是对开源精神的背叛;恰恰相反,它是防止开源项目落入上述两种悲惨结局的关键。
以下是我们本周发布的内容。
注重隐私优先的分析 API
PR #5266 用上面展示的通用提供者 SPI 替换了旧的 Google Analytics v1 AnalyticsService。你向 Analytics 外观注册一个或多个 AnalyticsProvider 实现,它会将 screen、event、setUserProperty 和 crash 调用分发到所有这些提供者,但仅在一个默认选择加入且在重启后保持用户选择的同意门之后进行。客户端 ID 是假名的且可重置,并且不涉及硬件标识符。
内置了五个提供者:我们的第一方 CodenameOneAnalyticsProvider(分批上传到云端,包含在所有付费订阅中,直至基础级别,计划决定你的数据保留窗口)、适用于 GA4 的 GoogleAnalyticsProvider、注重隐私的非 Google 选项 MatomoAnalyticsProvider、FirebaseAnalyticsProvider,以及用于开发的 LoggingAnalyticsProvider。旧的 AnalyticsService 仍然存在,已弃用,现在委托给新的 API,因此现有应用继续正常工作。完整介绍请参阅。
由你控制的地图,直至像素级别
PR #5264 将地图功能重新带回核心并进行了现代化改造,废弃了旧的基于瓦片的 MapComponent 和外部 Google Maps cn1lib。两个组件共享同一个 MapSurface API。我最感兴趣的是 MapView,一种完全通过 Graphics 渲染的纯矢量地图,其引擎基于框架自身的 ProtoReader 和 GeneralPath 构建了 Mapbox 矢量瓦片引擎。没有原生对等组件,这意味着没有 z-order 冲突、没有动画期间的快照妥协,并且在任何地方(包括模拟器和 Web)渲染效果一致。你自行托管瓦片数据和元数据,选择浅色或深色样式,并控制绘制的每一个像素。
[LOADING...]
第二个组件 NativeMap 为你提供来自 Apple MapKit 或 Google Maps 等提供者的真正原生地图,通过一个由构建提示而非代码选择的 SPI 进行连接。旧的 Google Maps cn1lib 将你绑定到单一供应商;现在不再如此。由于提供者在构建时注入,核心和端口不携带任何地图 SDK,未使用的提供者对项目体积零影响,而没有 Google Play 的设备(例如华为手机)只需一个构建提示即可获得可用的原生地图,而不是陷入移植死胡同。当未配置任何提供者时,NativeMap 会回退到嵌入式的 MapView。该引擎和提供者模型详见。
Apple TV、Android TV 以及识别外形因素的 CSS
PR #5261 增加了 Apple TV (tvOS) 和 Google TV (Android TV) 支持,其建模方式与上周的 Apple Watch 端口类似。你可以使用预期的 CN.isTV() 分支,在同一 Android APK 上运行于手机、平板和电视,而在 iOS 上则通过构建提示生成一个独立的 tvOS Xcode 目标。
在日常使用中影响最大的是样式层。CSS 引擎现在将设备外形因素理解为 @media 变体,因此你可以在样式表中而非分支代码中适应客厅十英尺视图或手表视图:
这项工作还完成了为现有手表端口(之前没有)接入 @media 的功能。一旦你将 codename1.tvMain 指向 tvOS 目标,Apple TV 就会从同一个项目构建,并且框架的截图套件已经能够在 Apple TV 模拟器上渲染完整的组件集。外形因素 API 和 @media 样式,以及运行在电视上的真实 Codename One UI,详见。
富文本与代码编辑
PR #5272 增加了两个可视化编辑器。RichTextArea 是一个所见即所得的 HTML 编辑器,支持粗体、斜体、列表、链接、颜色、标题,并提供 getHtml 和 setHtml 方法。CodeEditor 是一个 IDE 风格的编辑器,支持八种语言的语法高亮、行号槽、亮色和暗色主题、括号和引号自动闭合、异步代码补全,以及以波浪线下划线形式渲染的诊断信息,并配有槽标记和工具提示。
[LOADING...]
两者都基于同一个 AbstractEditorComponent,具有两个可互换的后端:一个是核心 jar 内部由自包含 HTML 和 JS 组成的完全跨平台引擎,另一个是由端口提供的可选更丰富的原生后端。CodeEditor 并非玩具演示:Playground 已经使用它了。深入介绍在。
设备完整性与应用评价
两个较小的 API 完成了本周的工作,它们都构建在核心中而非作为 cn1lib。PR #5277 增加了 DeviceIntegrity,这是一个针对高安全性应用的可移植运行时自我保护 API:Play Integrity 和 iOS App Attest 认证、root/越狱和 Frida 检测,以及无障碍服务滥用防护,其中大部分由构建提示驱动,并辅以运行时 API。我们已经拥有多个银行和支付领域的客户,Codename One 经过强化以满足他们带来的要求;此 API 是该工作的一部分。PR #5268 增加了 AppReview,它在存在该功能的平台上使用原生的应用内评价提示,在其他地方回退到内置的小部件,并带有反馈分流功能,悄悄地将不满意的用户引导至你,而不是导向一星的公开评价。
[LOADING...]
两者均在 中介绍。
JavaScript 端口的现状
Playground 现在基于我们自己的 JavaScript 端口构建。PR #5250 将其从固定的旧版本迁移到基于 ParparVM 的 JavaScript 端口,这与 Initializr 现在使用的路径相同。明显的成果是 Playground 直接跟踪当前 API,因此新的 API 演示(包括 3D 演示)可以在浏览器中运行,无需特殊构建。
做到这一步就像打地鼠一样:Playground 的反射访问注册表引用了几乎整个 API,这锻炼了普通应用从未触及的路径,而每一条路径都暴露了其自身的翻译器 bug。例如,byte[].class 这样的数组类字面量会抛出 Unsupported ldc constant,因为 JS 后端只处理对象类字面量。在发射过程中捕获的 Throwable 仅当它是 Exception 时才被重新抛出,因此 OutOfMemoryError 可能导致翻译器以截断、损坏的包成功退出。可变参数数组会产生格式错误的 new byte[][len] 而非 new byte[len][]。这些单独看来都不算奇特;Playground 只是同时遇到它们全部而已。
这是我们构建过的最困难的端口,每提高一个百分点的兼容性都是通过修复一个又一个 bug 换来的。JavaScript 端口现在与其他端口保持一致;剩下的工作是让它达到生产就绪状态,我们正处于最后阶段。我们希望在不久的将来实现这一目标。敬请期待。
来自社区
如果你在博客、Stack Overflow、论坛上撰写关于 Codename One 的文章,这正是我们最感激的季节。社区活动在夏季会放缓,而我们在顶部描述的服务部分依赖于人们首先找到这个项目。推荐好的文章,以及你自己创作的新文章,两者都有帮助。
即将推出的内容
我们将有一周的深度功能解读:
- 周六。 地图。PR #5264。
- 周日。 富文本与代码编辑。PR #5272。
- 周一。 分析。PR #5266。
- 周二。 Apple TV 与 Android TV。PR #5261。
- 周三。 设备完整性与应用评价。PR #5277 和 #5268。
- 周四。 关于过去、现在和未来的主题。PR #5253。
总结
联系我们最好的方式是在本文的评论中,包括你对下一个可选服务的请求。问题追踪器和讨论论坛也都在那里,构建云控制台位于。Playground、Initializr 和 Skin Designer 一直就在那里。
本文《无诱饵无换挡的开源资助:分析、原生地图、电视及更多》首发于 foojay。