搜索文档

输入关键词查找当前使用指南和维护文档。

Skip to content
中州鹅 · 历史记录与验证证据文档目录

结论、提交、测试结果与安装包均对应文中记录时点。日常操作以现行维护手册为准。

首键接收、会话复用与首次选词 ​

本次是在用户授权后实施的修改,承接旧原型审查 与真机日志分析。此前两份文档是修改前的审查记录。 上轮重绑改进的对照固定为修改前直连分支 2f6a0ea08527831d3f07df12499f5b72d5ffc0bd。 本轮已补做含 Fcitx 默认版的首次手指点选对照及系统 trace,见 首次候选点选报告。

后续首次点选调查: 早先的默认版对照测的是预热连续输入与空格选词,不能据此回答 Fcitx 版有没有“第一次手指点选慢”。本报告原先约 139ms 的首次点选是包含注入、按住、轮询的 模拟器观察值,不能全部归因于输入法,也不能直接称为用户真机可感知延迟。 新的候选点选工具使用含 Fcitx 的默认分支历史版本 cecea494 和直连 e6d31718,在独立编辑器进程记录抬手派发、文字变化及 onDraw, 部署后再停止进程,重复首次点选。结果确认原 Fcitx 也有首次成本;小样本尚未证明 缓存修正带来首次整体上屏的稳定改善,具体分段数字与目标编辑器排版证据见上述新报告。

用户补充与日志结论 ​

用户补充的复现场景是:唤起后立即打字,编码和候选不跟手;第一次候选点选也慢, 重复两次后恢复。它同时涉及绑定、首次查询和选词,不能只测 create_session。

原日志来自已经运行的同一进程,有 11 次 onStartInput,大量文件选择器和本应用之间的重绑。 相邻回调与候选状态日志之间的 509–1189ms 不是逐键耗时;原记录没有阶段关联, 不能据此断言模型缺页、GC 或厂商 E 级日志就是根因。此次修正有源码依据的重复工作, 并补上新构建的阶段计时,没有把旧日志的嫌疑写成已经证明的因果。

本次实现 ​

1. 按键到来时先登记,沿原队列顺序交付 ​

输入服务在 onStartInput 中同步登记编辑器代次、应用、输入能力及是否需要 Rime, 再异步绑定原生会话。启动及绑定尚未完成时,软键和硬件键可取得该代次的 ticket, 进入现有服务顺序队列,等待引擎和编辑器都可用后处理。

没有新增第二条按键队列,也没有在主线程等待 native。启动不再创建临时 session, 也不再额外递增代次,使已经登记的首键失效。Ready 后重放当前 Android 绑定是幂等的。

等待绑定的按键最多 128 个事件、最长 5 秒;超过边界会记录拒绝/未交付日志, 不允许很久之前的输入突然写入。编辑器切换、解绑、停止及手动部署会立即作废旧 ticket。 手动部署/目录替换期间继续拦截新键,不在完成后回放。

2. 逻辑编辑器代次与原生 session 分开 ​

同一个非空应用标识内,普通文本绑定可以复用一个仍有效的 native session。 每个新代次仍清理 composition、候选、输入标签和未交付 commit,重新设置 client_app 和 _no_learning。旧代次的按键、动作和输出不能交付新输入框。

失焦时清理并停用 session,保留其已加载组件;不再每次销毁、重建整套 Lua/翻译器。 跨应用、匿名应用、进入或离开敏感输入、session 失效、部署及引擎停止时重建。 敏感边界保守重建,因为 clear_composition 不能清除任意 Lua 组件的私有运行状态。

普通文本复用会保留同应用内方案开关、组件缓存和方案自身的上下文状态; 这不是完整重置 Lua 虚拟机。万象上下文调频的已有学习记录仍按方案管理。 输入完成后最多保留一个 session,可能比立即销毁占用更多驻留内存;停止引擎会释放它。

3. 无编辑需求的回调不提前创建 session ​

严格的 TYPE_NULL + IME_NULL 静默回调只更新逻辑绑定、清空界面。 显式显示键盘或收到真实硬件字符时再启用 Rime;首键在启用前登记,保留终端输入路径。 不使用 initialSel=[-1,-1] 猜测是否为非编辑器。

另外,绑定时已设置目标学习标志,随后相同的 setCapFlags 不再重复进入 native、 读取一次完整快照。输入方案、LTS 模型、数据目录和万象配置不作性能性改写。

4. 候选点选与按键共用顺序队列 ​

横向和展开候选的点击也经过服务按键队列、捕获编辑器代次,避免另起异步任务与紧接着的 键盘输入竞争先后。候选动作仍保留快照检查。Ready 后重复登记同一绑定是空操作, 不会清掉尚未向 Android 转发完成的硬件 key-down/key-up 配对记录。

5. 队列推进不等待主线程恢复空闲 ​

首轮测试后检查发现:单项引擎任务本来就在后台执行,但 jobs.consumeEach { it.join() } 的消费协程在服务主线程。前一个任务结束后,启动下一个绑定/按键/候选任务仍可能等待 主线程调度。消费协程改为 Dispatchers.Default,保留同一 Channel 与逐项 join 的顺序和取消关系。 事件到界面、InputConnection 的处理仍走原主线程路径。

这消除了源码中一处不必要的 UI 调度依赖;首次界面绘制和方案查询自身的成本仍需分别测量。

6. 缓存读取不等待后台线程调度 ​

后续检查发现 FcitxConnection.runImmediately 声明在调用线程执行,但实现将完整生命周期 协程上下文(包含 Dispatchers.Default)传入 runBlocking。主线程读取 volatile 缓存也要先 调度后台 worker,再同步等待。预编辑空状态更新和键盘首次显示都会经过这条路径, 因此后台繁忙可以推迟界面刷新。这段代码在原 Fcitx 版也存在。

改为仅移除缓存调用的 dispatcher,保留生命周期 Job 和取消关系,在调用线程完成即时读取。 显式的 native 操作仍通过 onOwner/withContext(owner) 到 rime-main,不将模型查询搬到主线程。 不修改按键/候选排序、提交事件、不丢弃快照、不输入虚假预热内容、不改变万象学习行为。 新增测试验证 worker 被占用时缓存读取仍返回、生命周期取消仍拒绝执行、native 显式 owner 仍生效。此处确认的是调度阻塞机制;实际收益另以同场景数据评估。

新诊断怎么读 ​

标签/trace内容与边界
RimeLifecycle startup资产、配置、native 启动各阶段毫秒数
RimeLifecycle input_binding代次、inputType、imeOptions、是否需要 Rime、restarting
RimeLifecycle bind绑定耗时、实际新增/复用 session 次数、学习标志
RimeLifecycle binding_inactive非编辑绑定或失焦,未创建新 native session
RimeInputQueue等待绑定的键交付/拒绝及原因;不记录键值
RimeInputTiming key每次绑定前 3 个键及耗时较长的键:owner 等待、native、publish 微秒数
RimeInputTiming select每次绑定前 2 次选词:owner 等待、native + publish 微秒数
RimeInputTiming select_rejected会话/快照已变更,旧动作未执行
rime.direct.* traceinitialize、maintenance、create/reuse、process_key、snapshot、select_candidate

这些数字不包含实际帧呈现。服务队列等待与 owner 等待也应区分:owner_wait_us 从调用后端时起算,不包括更早的服务排队;等待绑定的 ticket 另有 wait_ms。 没有记录完整输入内容。可继续用维护手册中的 inputDiagnostics 与系统 trace 排查 UI 消费、 主线程或缺页;不能从 native 快直接推出打字画面已经流畅。

验证 ​

  • 原生契约测试覆盖复用、跨应用/匿名/敏感边界、旧代次拒绝、失焦后上下键拦截、 清除未交付 commit/标签、部署及停止后的重建,保留 ABI 与线程所有权检查。
  • JVM 的 InputKeyGateTest 覆盖启动首键、绑定尚未完成、跨编辑器、超时、容量释放、 部署拦截、启动失败和非编辑器拒绝。
  • 完整前端新增非编辑器转为实际输入的回归,以及六轮重绑后立即触摸 n、i、候选“你”。 每轮要求首键保留、候选一次点击上屏、无重复 commit。 另检查绑定重复登记不会吃掉硬件键的 release。
  • Rime immediate input and rebind comparison 使用同一界面测试分别构建固定修改前版本与当前版本, 只覆盖测试源码,不改对照产品代码。使用 v18.1.3 base、固定 LTS 哈希和用户 YAML; 与上一轮一样显式补入用户确认开启的 context_reorder。模型必须实际 mmap。

界面测试用第一次显示及后续 restartInput,只等待 Android 绑定回调,不等待 native ready、 不输入预热词。结果是注入触摸到观察到状态/编辑器的耗时,包含注入和轮询开销, 不是屏幕光子延迟。fixture 准备已启动引擎,因此也不是杀进程后的冷启动测试。 每个版本只有六轮,单个模拟器、固定先后顺序,不作稳定分位数或真机流畅度保证。

本地原生 ASan/UBSan 检查通过;当前受限执行环境不支持 LeakSanitizer 的 ptrace 扫描, 本地关闭 leak 扫描,CI 仍运行原有完整 sanitizer 命令。

首轮定向结果与后续修正依据 ​

首轮运行 39087275365 对比 2f6a0ea0 与 e65cee02,两版六轮输入、点选和唯一提交均通过,模型实际 mmap。 其原始数据已保留,未用后续结果覆盖。

观察指标(ms,含注入/轮询)修改前第 1 / 2 轮首轮修改后第 1 / 2 轮
首次触摸到编码状态可观察317.8 / 163.8299.4 / 114.8
首次触摸到候选 view 绑定(输入 n、i 两键)467.3 / 221.8372.1 / 164.9
点击候选到编辑器文字可观察151.0 / 72.4178.9 / 68.5

首轮新版本的六次绑定为首次创建一次(198ms),后五次实际复用(1/1/1/1/4ms)。 这支持复用减少了重复绑定工作,但首次点选没有改善,不能宣称首次卡顿已解决。 第一轮 n 的 native 部分约 42.4ms,首次选词 native + publish 约 11.1ms; 后端数字明显小于上述端到观察值,剩余时间可能包括注入、轮询、UI 工作及排队。 第一轮绑定回调到 bind 日志约 851ms,其中 bind 计时为 198ms;差额也不能直接当作某一个函数耗时。

因此补查并移除了第 5 项的主线程队列推进依赖,再运行同一对照。 这是一项有源码依据的修正,不把轮询测量中的差额全部归因于该依赖。

另外,普通 PR checkout 会构建临时合并提交,其间默认分支另有界面更新。 完整直连前端的 APK 与设备作业现明确 checkout PR head;普通 PR CI 继续验证合并结果。 定向性能流程始终独立 checkout 固定产品提交,并校验 APK 内构建哈希,避免把两者混为一个版本。

最终分支验证:e6d31718 ​

包含第 5 项的最终产品源码为 e6d317182e25d225a6128420542ea47d161c03b9, 后续文档提交不改变产品代码。所有检查通过:

  • 本地 477 项 JVM 测试、androidTest Kotlin 编译;原生 ASan/UBSan 契约检查。
  • PR CI:合并结果的 JVM、四 ABI Debug 与依赖/DEX/页对齐检查。
  • 直连前端 CI:原生完整 sanitizer、独立引擎 smoke, 精确分支源码的 ARM64 包、JVM 与 10 项前端设备测试,含实际触摸、硬件键配对与非编辑器启用。
  • 最终万象对照:两版各六轮立即输入和一次点选, 无重复提交,LTS 实际映射;原始结果。
观察指标(ms,含注入/轮询)修改前第 1 / 2 轮最终版本第 1 / 2 轮
首次触摸到编码状态可观察328.2 / 162.9273.7 / 152.1
首次触摸到候选 view 绑定(输入 n、i 两键)491.9 / 212.6325.2 / 198.4
点击候选到编辑器文字可观察153.1 / 69.2139.4 / 73.5

六轮观察值中位数分别为 163.7 → 113.5ms(编码)、221.2 → 164.5ms(两键到候选)、 67.5 → 66.1ms(候选点击)。编码/候选在这次重绑样本中有改善;点选总体接近, 首次点选仍比后四轮慢,没有足够证据宣称点选提速或冷启动卡顿已全部解决。

最终新版本的六次编辑绑定首次创建一次(219ms),后五次均复用(6/7/2/1/1ms)。 其首次绑定回调到 bind 完成约 259ms,其中 bind 计时 219ms;首次 n 的 native 部分 约 29.8ms、publish 0.7ms,首次选词 native + publish 9.7ms。 这进一步说明仅减少 session 创建不能解释所有首次 UI 等待。 两轮 CI 不是同一主机的交叉重复实验,不能用它们的差值量化主线程消费修正的单独收益。

最终安装包为 zhongzhougoose-responsive-e6d31718-arm64-v8a-debug.apk, Actions 下载。 包内构建哈希已核对为上述分支提交,应用名中州鹅、包名不变;只有 libdirect-rime.so 与 libc++_shared.so,无 Fcitx 原生依赖/旧后端类,64 位页对齐 16 KiB。 APK SHA-256 为 ef5019db5216ff36ab86036f15a253ea069ad98c1c5a0d793fd491a3970e9a9a。 签名、产物来源和完整验证摘要见验证记录。

尚未宣称解决的部分 ​

真正进程冷启动仍需初始化和维护检查,跨应用或敏感边界仍会创建组件。 首个模型查询、Lua 首次执行和模型页面冷缓存仍可能较慢;本次没有靠模拟输入、 偷偷学习或更换模型预热。需要结合新阶段日志和相同手机复测,决定是否继续处理 I/O、 主线程 UI 或方案内部耗时,不能将这次会话复用称作已经消除所有冷启动卡顿。