鸿蒙上能跑 Python 和 Manim 吗?2026 年把 Manim 0.21 移植到 HarmonyOS NEXT 的最新实录

2026 年 10 月 21 日 · 极坐标⋅XYZ

鸿蒙上能跑 Python 和 Manim 吗?2026 年把 Manim 0.21 移植到 HarmonyOS NEXT 的最新实录

能跑,但安卓上现成的东西一样都用不上,得从源码重新编。我们的「Manim教程」鸿蒙版这个月在华为应用市场上架了,App 里打包了 CPython 3.13.9 和 Manim 社区版 0.21.0,用户写的 Manim 代码在手机本机渲染成 MP4。这篇记一下这一路是怎么搬的、在哪儿摔过跤,给也想在鸿蒙 App 里嵌 Python 的同行参考。

Manim 是 3Blue1Brown 用来做数学视频的动画引擎,Python 写场景,Cairo 画每一帧,最后交给 FFmpeg 编成视频,公式要过一遍 LaTeX。2026 年它在 PyPI 上的最新版是 0.22.1,我们 App 里固定在 0.21.0。想学 Manim 本身,2026 最新的中文教程在极坐标⋅XYZ,网页和 App 里是同一套课。

为什么不能直接拿安卓的

HarmonyOS NEXT 不再跑 APK,安卓 App 里的那些 .so 都是照着安卓的 libc 和系统库链出来的,放进鸿蒙用不了。我们 iOS 版和安卓版都有自己的一套 Python 运行库,到了鸿蒙只能用 DevEco 带的 OHOS Native SDK,对 arm64 从源码再编一遍:

  • CPython 3.13.9
  • NumPy 2.4.4
  • Cairo 1.16 和 Pycairo
  • Pillow 12.1.1(只开了 PNG 和 zlib,JPEG、WebP 这些还没编)
  • QuickJS,后面用来跑 MathJax

Python 自己也裁过:没有 SSL、ctypes、SQLite、lzma,也没有桌面 GUI 那一套。Manim 依赖链里有个 watchdog,默认用 Linux 的 inotify,而 inotify 那条路要 ctypes,我们让它用自带的轮询版 PollingObserver。

纯 Python 的包(manim 本体、networkx 这些)直接装进资源目录就行,麻烦全在带 C 扩展的包上。

在 ArkTS 里怎么把 Python 叫起来

App 是 ArkTS 写的,Python 解释器跑在一个 N-API 的原生工作线程里,App 启动后只初始化一次,之后每次渲染拿 GIL 跑用户的场景。界面线程从头到尾不碰 Python,渲染再久界面也不会卡住。

C 扩展的加载卡过一阵。鸿蒙不允许从 App 可写的数据目录加载 .so,所以 NumPy、Pycairo 这些扩展都得放进 HAP 包的原生库目录,再生成一张模块名到文件的映射表,让 Python 按表去找。另外鸿蒙的动态链接有命名空间隔离,扩展模块必须在链接时显式依赖 libpython3.13.so,不然加载时会报找不到符号。

还有个小细节:运行库按内容算一个 buildId,第一次启动解压到本地,以后版本没变就直接复用,不用每次启动都把整个运行库重写一遍。

没有 LaTeX、没有 Pango,文字和公式怎么出

桌面版 Manim 的 Text 靠 Pango 排版,MathTex 靠调用 LaTeX。手机上这两样都没有。

文字这边,我们用 FontTools 直接读 App 里自带的思源黑体(Noto Sans CJK)的字形轮廓,转成 SVG 交给 Manim。中英文、数字、多行、粗体斜体、分段上色都能做。代价是没有 HarfBuzz 这样的排版引擎,彩色 emoji、阿拉伯文这类从右往左排的文字、需要复杂字形组合的文字,我们选择直接报错,不出一张看着像对其实错了的图。

公式这边,用 QuickJS 跑 MathJax 3.2.1,把 TeX 转成 SVG。这份 MathJax 包跟 iOS 版共用一份。所以 MathJax 认识的 TeX 都能写,任意 LaTeX 宏包就不行了,写错的公式会报错。

Manim 有几处几何运算要用到 SciPy 和布尔运算库,这部分沿用了 iOS 版写好的兼容实现,没有重复造。

视频编码:硬件优先,失败就换软件

桌面版 Manim 把帧通过管道喂给 FFmpeg。手机上没有 FFmpeg,我们自己接了一条:

  1. Cairo 画出的每一帧是 RGBA,逐帧 gzip 压缩后写到临时文件。Manim 的画面大多是纯色背景加几何图形,压缩率很高:在电脑上试过一段 720p、30 帧、60 秒的几何动画,1800 帧裸 RGBA 是 6.64 GB,压完 45.1 MB。
  2. 原生层把帧转成 NV12,交给鸿蒙系统的 AVCodec 做 H.264 硬件编码,再用 AVMuxer 写成 MP4。
  3. 系统编码器创建不了、初始化失败、中途出错或者超时,就自动换成 App 里静态链接的 OpenH264 2.6.0,从第一帧开始用软件重编一遍。

第 3 步里「中途出错也回退」是吃了亏才补的。1.0.0 送审的时候,审核用的真机上所有渲染都失败,报 Cannot write MP4 frame,可我们在模拟器上测的时候好好的。模拟器上系统编码器根本创建不出来,一开始就走了软件编码,硬件那条路其实没被真正跑过。查下来我们的推测是:原先的写法要等编码器回调 onStreamChanged 才去建 MP4 的轨道,有的真机硬件编码器不先回调这个,第一帧编出来时封装器还没启动。现在的做法是编码开始前就把轨道建好,系统编码器那条路只要没拿到编码配置数据就当失败处理,回退到软件编码。

另外两件跟编码有关的小事:

  • 渲染前先用 skip_animations 把 construct 空跑一遍,按 Manim 自己的帧数公式估出总帧数,进度条才能显示百分比,iOS 版也是这么做的。
  • 免费用户导出的视频带一个 App 角标。角标在编码时用同一份 RGBA 帧再编一遍叠上去,预览里看到的永远是干净版。

为了不把手机撑爆,中间文件压缩后上限 1 GiB,Python 渲染和原生编码各自最多跑 300 秒,超了直接报错并删掉写了一半的 MP4。

目前的边界

  • 运行库只有 arm64。
  • 不支持 OpenGL 渲染器,用的是 Cairo。
  • 不能合成音轨,add_sound() 会报错。
  • Manim 0.21 新加的 Typst 公式,编译器在电脑上编出来了,还没上真机验证,这一版没放进去。
  • 鸿蒙真机上的渲染速度,我们还没有拿得出手的测试数据。

对用的人来说意味着什么

折腾这一圈,最后用户看到的就是:在鸿蒙手机或平板上装一个 App,打开课程,看到视频下面的代码跟着走,点一下带进工作区,改完点运行,进度条走完,手机上就是一段 MP4。不用电脑,不用装 Python,地铁上没信号也能渲染。

「Manim教程」鸿蒙版手机界面三联:左边视频课播放时下方代码跟着视频同步高亮,中间是工作区预览,圆周角定理动画在手机本机渲染成 1280×720 30fps 的 MP4,右边是 AI 助手做完动画后的汇报

常见问题

Q:能不能把这套运行库单独拿出来,在别的鸿蒙 App 里跑 Python? A:目前没有单独发布。它和 Manim 的渲染流程绑得比较紧,以后整理好了再说。

Q:为什么不在服务器上渲染,App 只负责显示? A:用户写的是任意 Python 代码,放到服务器上要做隔离、要排队、要花钱,网络不好还用不了。放在手机上跑,代码和视频不出手机,离线也能用。

Q:鸿蒙版和 iOS 版跑出来的画面一样吗? A:Manim 版本一样,几何和动画是同一套代码算的;文字排版方式不同,字体和字距不保证逐像素一致。


想试的话,在华为应用市场搜「Manim教程」或打开下载页,前 4 节课免费,也可以先看这篇上线介绍。电脑上不用装任何东西,打开工作区贴一段 Manim 代码就能跑;想系统学,从教程第一课开始。

来源:极坐标⋅XYZ(jizuobiao.xyz),2026 年 10 月 21 日,10 月 30 日更新。文中版本号与数字来自「Manim教程」鸿蒙版 1.1 的运行库构建记录。