JavaFX 后台线程操作 ObservableList 报 IllegalStateException
现象做“导入历史数据”功能时,解析 Excel 是耗时操作,很自然放到后台线程。解析完直接往绑定了 TableView 的 ObservableList 里 add,结果运行时炸了:
Exception in thread "pool-1-thread-1" java.lang.IllegalStateException: Not on FX application thread; currentThread = pool-1-thread-1
原因JavaFX 有一条铁律:所有对 Scene Graph 的修改(包括 ObservableList、Property 的变更)必须在 JavaFX Application Thread 上完成。ObservableList 的变更会触发 UI 监听器刷新表格,后台线程改它就破坏了线程安全,所以框架直接抛异常拦截。
错误做法最开始图省事,每解析一条就 Platform.runLater 包一下 add:
thread { val parsed = parseRow(row) // 耗时,后 ...
用 Kotlin 协程替代 Thread 并行加载多测线雷达图
背景多测线雷达图加载,原来用 ExecutorService + Thread:每条测线一个任务,结果通过回调或 Future 收集。线程数靠拍脑袋,取消靠一个 volatile 标志位,异常处理各写各的,并行度也控制不好。串行加载几条测线时界面干等着,体感很慢。
改成协程换成 Kotlin 协程后,多测线并行加载就一段:
suspend fun loadMultiScanLines(files: List<RadarFile>): List<RadarData> = coroutineScope { files.map { file -> async(Dispatchers.IO) { readRadar(file) } // 每条测线一个异步任务 }.awaitAll() // 并行,全部完成后返回}
对比 Thread 方案的收益:
结构化并发:所有 async 任务挂在同一个 coro ...
MVVM 重构 把数据加载从 View 下沉到 ViewModel
起因项目最早的写法是典型的“胖 Controller”:View(FXML 对应的 Controller)里既管 UI 交互,又直接调 Service 加载数据、刷新表格、维护选中状态。功能一多,单个 Controller 几千行,加载数据的逻辑和 UI 操作揉在一起,几乎没法单元测试,改一处怕动全身。
拆分按 MVVM 把职责切开:
View:只管 UI 绑定和用户交互,从 UI 组件里提取纯数据(比如表格当前选中的行),不含业务。
ViewModel:持有状态(Property / ObservableList),负责数据加载的调度和参数构建,对 View 暴露可绑定的属性。
Service:纯逻辑,不碰 JavaFX,可在任意线程跑、可单测。
核心动作是把数据加载从 View 剥离到 ViewModel。原来 Controller 里 service.load() 之后跟一堆 UI 刷新,现在变成 ViewModel 加载完更新自己的 ObservableList,View 通过绑定自动刷新:
class RadarViewModel { val f ...
twikoo找回管理员密码
twikoo找回管理员密码前言距离上一次维护网站,已经过去很长很长一段时间了。中间那阵子,我一直在调整自己的生活状态,后来又搬到了另一个城市生活,更是完全没了心思去管网站的事。一直到去年年底,域名和服务器同时到期——服务器咬咬牙还是续上了,域名却换成了一个便宜的新域名。之后就是一点一点地把网站内容往新域名迁移,又折腾了新域名的备案。最近备案总算通过,网站也终于重新上线。可结果,发现 twikoo 的暗号和管理员密码早已被忘得一干二净。于是,便有了这篇关于「twikoo 找回管理员密码」的记录。
部署方案说明我的 Twikoo 是通过 Vercel 部署的,数据库用的是 MongoDB。接下来的操作步骤和截图,也都是基于这套环境来展开的。
浏览器控制台找回暗号(HIDE_ADMIN_CRYPT)先解决「暗号」的问题。这个其实不用去翻数据库,直接在浏览器控制台就能看到:
打开网站首页,按 F12 打开开发者工具,切到 Network 面板。
刷新页面,等 twikoo 加载完成后,在 Network 的过滤框里搜索 HIDE_ADMIN_CRYPT。
进到 MongoDB 查看 tw ...
用 jlink 给 JavaFX 桌面应用生成精简自定义 JRE
为什么用 jlink桌面应用分发的痛点:用户机器上不一定有 JDK,让用户自己装 25 既不现实也不优雅。jlink 可以把“应用真正用到的那些 jmod”拼成一个精简的、独立的 JRE,和应用一起打包,用户双击就能跑,体积也比塞一个完整 JDK 小很多。
准备需要两套 jmods:
JDK 自带的 jmods:在 $JDK_HOME/jmods(openjdk-25 自带)。
JavaFX 的 jmods:要单独下 javafx-jmods-25.0.1,SDK 包里没有,得另找。
两套都必须是 25,版本不一致 jlink 会直接拒绝链接。
命令核心就是一条 jlink,把 --module-path 指向两个 jmods 目录,--add-modules 列出实际需要的模块:
%JDK_HOME%\bin\jlink ^ --module-path "%JDK_HOME%\jmods;%JAVAFX_JMODS%" ^ --add-modules java.base,java.management,jdk.charsets,javafx.control ...
Kotlin 数据类型
Kotlin 数据类型使用var定义变量
var b: Int = 1// val 创建常量val a = 1
所有的数据类型都是对象 Int
public actual class Int private constructor() : Number(), Comparable<Int>public actual abstract class Number { /** * Returns the value of this number as a [Double], which may involve rounding. */ public actual abstract fun toDouble(): Double /** * Returns the value of this number as a [Float], which may involve rounding. */ public actual abstract fun toFloat(): Float /** * Retur ...
Docker 部署 File Browser 文件管理系统
Docker 部署 File Browser 文件管理系统1. File Browser 介绍File Browser 是一款基于 Go 语言开发的开源、轻量级、跨平台的 Web 文件管理工具。
它允许用户通过浏览器访问和管理服务器上的文件,支持 Windows、macOS、Linux 等多种操作系统。
FileBrowser 具备文件的上传、下载、复制、移动、重命名、在线预览及编辑等功能,还提供多用户支持与精细的权限控制,可设置不同用户对文件和目录的访问权限。
此外,它支持连接 FTP、SFTP、S3 等远程存储,方便用户管理远程文件,并且能够通过生成带密码或有效期的外链来实现文件分享。
官网:https://filebrowser.org/GitHub:https://github.com/filebrowser/filebrowser
官网在线体验地址:https://demo.filebrowser.org/用户名: demo密码: demo
2. 安装以下是使用Docker安装的步骤:
创建本地存储目录
# File Browser的数据文件存储目录mkdir -p /my ...
JavaFX初学
JavaFX初学1. Scene Graph(场景图)JavaFX 的场景图是一个树状数据结构,表示应用程序的所有 UI 组件。
层次结构:Stage (舞台) → Scene (场景) → Parent (根节点) → Nodes (子节点)
2. 事件机制JavaFX 使用事件驱动编程模型,基于观察者模式。
主要事件类型:
ActionEvent:按钮点击等
MouseEvent:鼠标相关事件
KeyEvent:键盘事件
WindowEvent:窗口事件
3. 常用容器和布局方法VBox - 垂直排列
VBox vbox = new VBox(10); // 10像素间距vbox.setPadding(new Insets(15)); // 内边距vbox.setAlignment(Pos.CENTER);
HBox - 水平排列
HBox hbox = new HBox(10);hbox.setPadding(new Insets(10));hbox.setAlignment(Pos.CENTER);
BorderPane
BorderPane borderPane = ...
从零搭建 JavaFX 25 与 Kotlin 2.3 桌面应用踩坑记
背景最近在做一个 JavaFX 桌面应用(数据可视化分析类),出于对新特性的尝鲜和长期维护考虑,技术栈直接拉到了最新的 JDK 25 + Kotlin 2.3 + JavaFX 25。结果就是,踩了一整圈版本对齐和模块系统的坑,记录一下。
版本必须严格对齐这套组合对版本非常敏感,任何一个对不上都会报各种莫名其妙的错:
组件
版本
为什么必须是这个版本
JDK
25
JVM Target 统一为 25
Kotlin
2.3.0
第一个正式支持生成 Java 25 字节码的版本
Gradle
9.1+
低版本 Gradle 不认 JDK 25
JavaFX
25.0.1
与 JDK 25 配套
IDEA
2025.3+
旧版对 Kotlin 2.3 支持不全
Kotlin 版本低于 2.3 时,编译器无法把字节码目标设到 25,会直接报编译错误;Gradle 低于 9.1 时连 java.toolchain 都识别不了 JDK 25。这俩一旦对不上,报错信息往往指东打西,很难第一时间定位到是版本问题。
模块系统:–add-opens 是家常便饭JavaFX ...
hexo+butterfly目录显示问题
Hexo+butterfly博客网站文章目录编号异常写完文章后在Typora中查看大纲目录正常,但是推送后在网页看到数字编号出现重复问题,如下图
其实就是butterfly这些主题框架都内置了一个目录编号,在butterfly的_config.yml文件中找到toc中的number属性并修改为false即可。
好像next也有这样子的情况,应该也是修改配置文件中的这个属性就可以。
原属性值为true,会自动为各级标题添加编号并在目录显示,如果没有写标号的习惯还想要也可以设置为true
