Ohhnews

分类导航

$ cd ..
foojay原文

第三代GUI构建器:一个工作区覆盖所有表单

#codename one#gui构建器#maven#跨平台开发#可视化编辑器

[LOADING...]

我们再次重建了 Codename One GUI 构建器。这是它的第三代。有意思的不是又一个拖放界面,而是我们保留了什么、Maven 打破了什么,以及为什么可视化编辑器必须理解整个项目,而不是一次打开一个生成的表单。

与 Steve Hannah 构建的第二代相比,这是一次规模更小的重写。他的引导式布局工作仍然是基础。PR #5523 用 Maven 优先的 Codename One 应用替换了外围外壳,让你无需离开工作区即可在表单、CSS 和 Java 之间切换。

本周一览

  • 第三代 GUI 构建器 现在是一个项目工作区,通过 mvn cn1:guibuilder 启动。
  • 应用加固 在平台构建拆分之前应用重命名、字符串加密和选定的控制流变换。后续文章将于周六发布。
  • iOS 设备端调试器 不再在复用的局部槽位被误认为对象时崩溃。
  • 平台状态和 Linux 视频 现在会报告跳过的测试,而不是将其计为通过。同一工作还修复了三个 GStreamer 缺陷。
  • Google 登录 升级到 7.1 版本,因此 Apple Silicon 模拟器可以获得真正的 arm64 模拟器切片。
  • ParparVM 内存回收 现在会在小对象峰值过后将多余的 BiBOP 页面归还给操作系统。
  • 缺失和遗留的 URL 现在会指向恢复页面,而不是赤裸裸的 404。

三代构建器,三个不同的项目

第一个 GUI 构建器源自我们在 Sun Microsystems 开发的工具。它将表单存储在资源文件中,并生成一个中央状态机。当 Codename One 仍然面向大约 2 MB 内存的功能机时,这个模型是合理的。

第二代将表单分离为可读的 .gui XML 和配套的 Java。Steve 在 LayeredLayout 之上构建了自动布局,包括智能边距、兄弟引用、尺寸匹配、基线对齐和多选。对于现代应用来说,这是一个好得多的模型,但编辑器仍然从选定的表单打开。

最后一个假设在 Maven 迁移后变得痛苦。一个 Maven 项目在 src/main/guibuilder 下有表单,在 src/main/java 下有 Java,在 src/main/css/theme.css 下有样式。为每个生成的表单启动一个单独的编辑器,使得导航成为可视化工具中最慢的部分。

[LOADING...]

第三代将工作单元从文件改为项目。它扫描每个 .gui 文件,将其保存在左侧面板中,在中心渲染选定的表单,并在右侧显示其属性、布局和事件。

[LOADING...]

一个 Maven 目标即可打开项目

创建表单并从项目根目录打开编辑器:

$ bash
mvn cn1:create-gui-form -DclassName=com.example.ProfileForm
mvn cn1:guibuilder -DclassName=com.example.ProfileForm

第一个目标创建属于版本控制的成对文件:

common/src/main/guibuilder/com/example/ProfileForm.gui
common/src/main/java/com/example/ProfileForm.java

第二个目标通过 Maven 解析 com.codenameone:codenameone-guibuilder,并将项目绑定传递给编辑器。IntelliJ IDEA、NetBeans、Eclipse 和 Visual Studio Code 现在都提供了调用同一目标的快捷键。编辑器本身仍然是 Java 8 构件,因此它可以在已经构建应用的 JDK 上运行。

切换表单不再启动另一个进程。表单选项卡由 src/main/guibuilder 的递归扫描支持,因此一个有二十个表单的项目就像一个有二十个表单的项目,而不是二十个互不相关的编辑器会话。

引导式布局在重写中幸存下来

我们没有用绝对坐标替换 Steve 的布局模型。在自动布局模式下放置的组件仍然是 LayeredLayout 子项,其边距可以引用父组件或另一个命名组件。

这是构建器演示项目中裁剪后的表单:

$ xml

这些名称很重要。以对象指针形式存储的引导会在保存并重新加载后消失。以组件名称存储的引导会保留在磁盘上,这意味着模型必须拒绝重复名称,并在重命名、删除或粘贴组件时更新引用。

画布在你拖拽时添加关系。它可以对齐边缘、中心和文本基线。调整大小可以保持首选尺寸、填充父容器、保持固定或匹配另一个组件。同一个画布可以从手机竖屏切换到桌面宽度,这使得糟糕的关系在到达设备之前就能显现。

[LOADING...]

上面的移动不是存储为一组新的绝对坐标。它改变了 primarydescription 之间的关系,然后让 LayeredLayout 为当前画布解析结果。

[LOADING...]

CSS 属于画布旁边

第二代构建器和旧资源编辑器是独立的工具。用户在我们 2016 年 GUI 构建器文章中的评论要求编辑器读取项目 CSS 并直接渲染它。第三代终于将其视为正常的工作流程。

点击 CSS,项目样式表会在实时表单旁边打开:

[LOADING...]

该窗格编辑真正的 src/main/css/theme.css。编辑去抖后,CSS 编译器安装新主题并重建预览。选择器变更会在相应元素上直观呈现:

$ style
Button {
    background-color: white;
    color: #2459b8;
    border: 1px solid #315fce;
    border-radius: 2mm;
    padding: 2mm 4mm;
}

没有第二个需要同步的主题模型。为画布着色的样式表就是 Maven 为应用编译的样式表。

生成的 Java 可见,但不可随意丢弃

Code 按钮会在同一工作区中打开配套的 Java 源码:

[LOADING...]

生成代码和用户代码有不同的归属:

$ java
// 
private Button signIn;

private void buildUI() {
    signIn = new Button("Sign in");
    signIn.setName("signIn");
    add(signIn);
}
// 

// 
protected void onSignIn(ActionEvent event) {
    authenticate();
}
// 

嵌入式编辑器保护生成区域,而不是等到保存时覆盖它。事件处理程序留在用户区域并在重新生成后幸存。这消除了一个旧的时序问题,即一个 IDE 在保存时生成源码,而另一个 IDE 等待下一次构建。

.gui 文件仍然是纯 XML。你可以在拉取请求中审查它,并且无需专有数据库即可恢复它。当前编辑器在打开时不会监视手动编辑的 .gui 文件,因此请在外部编辑后使用 Refresh

平台拆分前的一个加固策略

PR #5527 添加了一个开源加固引擎,它在合并后的应用成为 Android、iOS、JavaScript、Windows、Linux 或 JavaSE 输出之前运行。目标是获得 DexGuard 级别的抵抗力,而不会保护一个平台却让其他平台暴露在外。

该策略有意感知平台。Android 保留 R8 作为其唯一的重命名工具。JavaScript 跳过字符串加密,因为其原生桥接可以持有实时字符串引用。ParparVM 跳过与其优化器冲突的控制流变换。公共流水线仍然提供一个构建提示、一份报告和一个与崩溃保护关联的构建特定映射。

周六的 应用加固深度解析 涵盖了确切的平台矩阵、字符串加密排除项、保留规则、retrace 生命周期和本地构建边界。它还将加固与 App Shield 以及仍在审查中的加密数据库工作区分开来。

断点不再解引用一个随机局部变量

PR #5536 修复了当 NetBeans 在断点处请求局部变量时可能导致 iOS 应用崩溃的 signal 11 崩溃。一个调试器表为每个 JVM 槽位存储一个地址,但每个声明的局部变量占一行。将槽位复用于 int 后再用于对象,可能会将对象行与四个字节的整数存储配对,然后将其作为八个字节的对象指针解引用。

生成的表现在每行存储一个地址。帧进入会清除过期的调试器侧信道,对象引用在原生代码解引用之前会通过 Darwin 内存读取检查。同一 PR 还添加了真正的线程枚举、延迟断点重放、作用域局部变量和现成的设备端调试操作。

该 PR 围绕生成的 C 和 JDWP 代理添加了 66 个测试。它没有包含连接设备的调试会话,因此合并证明了策略和生成代码,而不是每个 IDE 和设备组合。

跳过的测试不是通过

PR #5538 发现截图跳过标记使用输出名称,而报告解析器只查看 Java 类名。标记消失了,周围的开始和结束行将测试计为通过。

纠正该映射暴露了 watchOS 上 11 个隐藏跳过、tvOS 上 6 个,以及其他每个平台上两到三个。未知标记现在会使报告契约失败,而不是消失。

同样的调查修复了 Linux VideoIO。CI 镜像缺少它声称要测试的编解码器插件。GStreamer 返回了一个部分流水线加上一个缺失元素的错误,但该平台忽略了该错误。读取器还向暂停的流水线请求普通样本,而解码帧仍然是预滚动缓冲区。Linux 现在安装编解码器、拒绝部分流水线、正确拉取预滚动帧,并且只报告 GStreamer 注册表中存在的编码器和解码器。

Google 登录现在可以在 arm64 模拟器中运行

PR #5544 将 iOS Google 登录集成从旧捆绑的 5.x 框架迁移到 GoogleSignIn 7.1。旧框架有 arm64 设备切片,但没有 arm64 模拟器切片。在 Apple Silicon Mac 上,链接器选择了设备切片并拒绝了它。

7.1 版本从源码为所选 SDK 构建。原生桥接现在使用完成处理器和当前令牌 API。我们停在 7.1,因为后续版本引入了 Swift 依赖,会强制将模块化头文件放入每个生成的 Podfile,包括与 Google 登录无关的项目。

小对象页面可以归还给操作系统

ParparVM 的 BiBOP 分配器按大小类别隔离小对象。在 PR #5540 之前,空页面永远留在该分配器中。因此,小对象的临时峰值可能会在之后排挤图像缓冲区、Metal 纹理或字形图集,即使小对象已被回收。

收集器现在保留一个 4 MB 的热池,并释放多余页面的槽位区域。在 Apple 平台上,它使用 MADV_FREE_REUSABLE,这会减少用于内存压力的 phys_footprint 值。一个受控集成测试预热了 192 MB 的 256 字节对象,然后分配了两组相同的大缓冲区集。测得的峰值从 466,224 KB 下降到 287,824 KB,而基准测试的几何平均值相对于无释放对照组保持在 1.0031。

Arm64 保留每个 64 KB 分配器页面的大约四分之一,因为其 16 KB 系统页面也包含分配器头。移动该头需要更大的分配器重新设计。

已退役的 URL 现在有了有用的去处

PR #5546 为旧插件和 Ant 时代链接添加了一个 /download/ 兼容性页面。新项目转到 Initializr 和入门指南。现有遗留项目转到 Maven 迁移指南。

通用 404 页面现在提供搜索以及指向设置、文档、演示、定价、社区和遗留下载的直接路由。它被标记为 noindex, nofollow;普通页面保持可索引。受约束的 _redirects 文件没有改变。

Initializr 现在通过 PR #5533 在 Codename One 7.0.265 上生成项目。

应用周边的工作是应用的一部分

GUI 构建器重写是关于让控制贴近你交付的构件。表单、CSS、生成的 Java 和响应式行为现在共享一个可检查的工作区。调试器、平台报告、内存分配器、登录桥接和恢复页面都消除了工具链之前隐藏真实状态的地方。

安全也遵循同样的方向。上周 App Shield 将最终信任决策从手机移到了后端。周六的 应用加固文章 涵盖了二进制层。开放的 可移植加密数据库 PR 解决了静态数据问题,目前仍在审查中。

这些控制解决不同的问题。它们共同将 Codename One 推向在每个边界都有安全默认值:二进制中的可读代码、调用服务器的被修改客户端,以及磁盘上的明文数据。与一个名字响亮的大复选框相比,这才是更有用的安全领先优势。

这篇文章 The Third-Generation GUI Builder: One Workspace for Every Form 首次出现在 foojay 上。