CodePocket 技术架构
面向开发者:引擎、JNI 桥、线程模型、状态机与目录结构。
1. 总体架构
┌───────────────────────────────────────────────┐
│ Compose UI 层 │
│ EditorScreen · EditorViewModel · OutputPanel │
└──────────────────┬────────────────────────────┘
│ RunEngine 接口(Flow<RunEvent>)
┌──────────────────▼────────────────────────────┐
│ NativeEngine(Kotlin) │
│ 状态机:busy/running/execActive/finishedSent │
│ sink:C 回调线程 → Flow 的事件出口 │
└──────────────────┬────────────────────────────┘
│ JNI
┌──────────────────▼────────────────────────────┐
│ pybridge.c(JNI 桥,约 350 行 C) │
│ Py_Initialize · PyRun_FileExFlags · GIL 管理 │
│ stdin/stdout/stderr 桥接 · Py_AddPendingCall │
└──────────────────┬────────────────────────────┘
│ dlopen + sys.path
┌──────────────────▼────────────────────────────┐
│ libpython3.13.so(ARM64)· C 扩展 · 标准库 zip │
└───────────────────────────────────────────────┘
2. 引擎模块(app/src/main/java/com/devterminal/engine/)
| 文件 | 职责 |
|---|---|
RunModels.kt |
RunRequest / RunEvent / Language 等运行契约 |
RunEngine.kt |
引擎接口:run / stop / writeStdin / closeStdin / release |
EngineProvider.kt |
工厂:运行时自检 → 创建 NativeEngine;失败给出可读错误 |
NativeEngine.kt |
原生引擎实现:初始化、执行、中断、状态机 |
NativeBridge.kt |
JNI 声明:initialize / execFile / pushInputLine / pushEof / requestInterrupt / shutdown |
EnvironmentInstaller.kt |
环境自检与安装(引擎相关部分) |
ExecutionService.kt |
前台服务:运行期间保活(Android 12+) |
GitManager.kt |
JGit 封装:init / commit / push / pull |
FriendlyError.kt |
20+ 高频错误的"人话"翻译 |
SyntaxCheck.kt |
轻量语法预检 |
CompletionProvider.kt |
静态补全:关键字 / 内置函数 / 文件符号 |
3. JNI 桥(app/src/main/cpp/pybridge.c)
初始化(initialize)
PyImport_AppendInittab("dtbridge", ...)注册桥模块(必须在Py_Initialize前);Py_Initialize();- 注入 Python 桥接代码:
_DtOut/_DtErr/_DtIn(继承io.TextIOBase)替换sys.stdout/stderr/stdin,输出经dtbridge._output回调到 Kotlin,input()经dtbridge._input读取; - 标准库路径注入
sys.path。
执行(execFile)
os.chdir(工作目录)+ 脚本目录插入sys.path(先去重,避免跨脚本串扰);- 构造
sys.argv = [脚本路径, ...args](用 C API,避免字符串转义); - 注入
__file__/__cached__/__loader__/__spec__(对齐 CPython 标准运行环境); PyRun_FileExFlags(fp, path, Py_file_input, ...)执行;- 错误时
PyErr_Fetch取异常文本返回给 Kotlin;成功返回NULL。
中断(requestInterrupt)
Py_AddPendingCall在字节码检查点抛KeyboardInterrupt;- 阻塞在
input()时:pushEof()让input()收到 EOFError 立即返回(顺序必须 EOF 先、中断后,否则 GIL 死锁)。
线程与 GIL
initialize在 IO 线程:Py_Initialize后PyEval_SaveThread释放 GIL,注入代码时PyGILState_Ensure重新持有;execFile在独立 IO 线程:PyGILState_Ensure/PyGILState_Release成对使用;- Python 输出线程回调 Kotlin 时
AttachCurrentThread→CallVoidMethod。
4. 运行状态机(NativeEngine.run)
关键约束:execFile 是阻塞 JNI 调用,协程 cancel 无法中断它。因此:
busy(AtomicBoolean) —— 是否允许新运行
running(@Volatile) —— 本次运行是否仍在执行
execActive(AtomicBoolean)—— C 层调用是否仍占用
finishedSent(AtomicBoolean)—— Finished 事件幂等去重
sink(AtomicReference) —— C 回调 → Flow 的事件出口
时序:
busy.compareAndSet(false, true)成功才继续,否则拒绝并发运行;- 发出
Started→ 设置sink→ 启动execFile协程 + 看门狗协程; execFile返回后:发Stderr(如有)→sendFinished(0/1);finally里才复位busy/running/execActive并清sink——保证旧脚本真正结束前不允许新运行;- 看门狗超时(
timeoutMs + 5s):推 EOF →requestInterrupt→ 等 2s → 仍未退出则警告并sendFinished(130); stop():推 EOF + 中断 + 2s 兜底线程(脚本不响应时补发Finished(130))。
5. 标准库与 C 扩展的打包
- 纯 Python 标准库:
assets/python-stdlib.zip(约 60MB,noCompress保证 AAPT 不压缩),首次运行解压到files/python-stdlib,校验标记.unpacked_ok; - C 扩展:
jniLibs/arm64-v8a/下的*.so(math、_json、_socket、_ssl、_sqlite3、_ctypes 等),APK 安装后落在nativeLibraryDir,initialize时插入sys.path; - 依赖库:
libssl_python.so/libcrypto_python.so/libsqlite3_python.so一并打包; - 重新打包/重编脚本:
scripts/package_native.sh(需 NDK 27.3 + CPython 3.13 交叉编译环境)。
6. 目录结构
app/src/main/
├── AndroidManifest.xml
├── assets/ # python-stdlib.zip、textmate 语法、模板等
├── cpp/pybridge.c # JNI 桥
├── java/com/devterminal/
│ ├── MainActivity.kt
│ ├── engine/ # 运行引擎全家桶
│ ├── project/ # ProjectManager、Templates、GitManager
│ ├── settings/ # SettingsStore(加密偏好)
│ └── ui/ # Compose 界面(EditorScreen 等)
└── jniLibs/arm64-v8a/ # libpython3.13.so、libpybridge.so、C 扩展
scripts/ # build_apk.sh、package_native.sh
tools/ # selfcheck.py、test_suite.py(Web 原型测试)
7. 构建与工具链
| 组件 | 版本/来源 |
|---|---|
| JDK | 17+ |
| Android SDK | platforms;android-35、build-tools;34.0.0 |
| Gradle | 8.11.1(wrapper 随仓库) |
| AGP / Kotlin | 8.7.3 / 2.0.21 |
| CPython | 3.13.9(官方 PEP 738 Android 支持) |
| NDK | 27.3.13750724 |
| 编辑器 | SoraEditor 0.23.6(LGPL-2.1,动态链接) |
| Git | JGit 6.10.1(纯 Java) |
依赖镜像:阿里云 Maven 优先,Google 备选;Gradle 发行包走腾讯云镜像。