普通视图

发现新文章,点击刷新页面。
昨天以前首页

X未提前通知,突然停用twitter授权登录域名,大量X三方登录异常!

作者 CocoaKier
2026年5月26日 11:23

昨天(5月25日)我们收到不少客诉反馈X授权登录异常,具体表现是:X授权登录过期后(网页登录态没过期不受影响),会弹下面页面“若要使用此应用程序,你必须登录X”,用户输入账号密码登录登录后又会回到此页面,无限循环。

iShot_2026-05-26_11.01.25.png

解决方案
(必须)修改 客户端\前端 X授权域名
https://twitter.com/i/oauth2/authorize => https://x.com/i/oauth2/authorize

(可选)修改服务端鉴权域名
https://api.twitter.com/2/oauth2/token => https://api.x.com/2/oauth2/token
——建议一起改一下,避免夜长梦多。这个我们测过了,换了没影响

请检查 客户端、Web站点、PC端、服务端,是否包含上述域名地址,请全部替换

如果上面地址是客户端写死的,哭吧,要更包,我昨晚已经加班更了4个包了😭

Tips: iOS清理X登录态方法:系统设置 - Safari - 清理所有历史记录(只清twitter.com、x.com也行)


问题原因分析
上面这个页面之前没见过,怀疑是X的程序员最近新加的,加出bug了,callback回调时忘记兼容老的twitter域名了。说不定后面他们发现后会修复,就不用更包了,但我们等不起也赌不起,万一人家懒得修呢?

参考资料:
docs.x.com/fundamental…

谷歌发布安卓 AI 系统,这就是苹果想象中的自己

作者 马扶摇
2026年5月13日 12:00

和去年一样,在正式的 Google I/O 开发者大会之前,谷歌为 Android 单独开了一次小型发布会。

本次 Android Show 上,谷歌几乎是一股脑将未来一年有关 Android 和整个 AI 产品生态的「宏愿」抖了出来。

除了作为基底的 Android 17 之外,我们这次还迎来了一些意料之外的平台更新和几款硬件产品的发布。

重点在于:虽然产品不多、距离 Android 17 广泛推送还有一段时间,但我们已经足以看到谷歌未来五年甚至十年,对于人工智能生态的计划了。

更要命的是,今晚的谷歌,刚好是苹果梦想里进入 AI 时代之后的自己。

操作系统到智能系统

活动刚开场,谷歌就宣布了一项意义重大的举措:

Android 将不再是一个单纯的操作系统(Operating System),而是一个智能系统(Intelligence System)。

图|Google

换句话说,曾经以「用户手动操作」为绝对主流的软硬件使用方式,在谷歌看来已经是上一个时代的符号了。

从今天起,Android 作为一个智能功能的集群,会更加主动地介入到用户操作流程的更前端,想你所想、做你所做。

图|Google

这也是为什么在本次活动上,Android 17 这个具体的系统版本出现的次数屈指可数,Gemini Intelligence 却成为了系统的代名词。

首先,Gemini Intelligence 作为谷歌 Gemini 在手机硬件上的最新形态,极大强化了它多模态、跨环境、高度整合的运行模式。

比如作为系统输入的第一入口,默认键盘 Gboard 就得到了一次功能强化。

基于 Gemini Intelligence 的多模态能力,原本在键盘功能中存在感不高的「自动填入」功能极大地拓展了它的信息来源:

图|Google

除了手动保存的各种密码之外,Gboard 还将会支持自动填入图库里面的证照信息、聊天提到的地址信息、邮件撰写的日程信息等等。

更直白地说:Gemini Intelligence 已经远超「帮忙记住密码」的水平,而是真正像个助理一样帮你记住和建议各种来源、各种类型的信息。

另一种有效利用这些多模态信息的方式,则是 Android 的桌面小组件(widget)。

在 Android 17 中,Gemini Intelligence 将会支持一项名为「Create my widget」的功能,但不是第一时间上线、而是目标今年晚些时候。

图|Google

这个新功能主要做的,就是用类似 vibe coding 的模式,根据你的指令在桌面上创建新的小组件,打破了小组件只能是 app 预置的那些。

举例来说,相比功能单一的记录卡路里的 app,我可以和 Gemini 说:做一个每周工作日向我推荐两次高蛋白餐的小组件。

图|Google

这样一来,桌面小组件就真正变成了一项复合任务的入口,本质上和人 vibe coding 一个 app 的性质是完全相同的。

此外,喜欢语音输入的用户也有福了——新版 Gboard 将会支持类似 Typeless 的高智能化语音输入功能,名字叫做 Rambler。

相比以前要亲口说「逗号…句号…」,Rambler 可以将一整段充满了「嗯嗯啊啊」的口述转译、清洗、整理成一段整洁的文字:

图|Google

另一方面,Gemini Intelligence 的自动执行功能也得到了进一步加强。

去年的 Google I/O 和发布会上,谷歌演示过给 Gemini 下命令,让它自动帮你点外卖、叫车、订票之类的操作,正式上线之后反响不错。

而在 Android 17 中,Gemini 升级成 Gemini Intelligence,这种「代操作」也支持多步骤任务了。

比如以前只支持简单的「帮我订一张票」,你现在可以在 Gemini 对话框里直接拍下旅游宣传册,和 Gemini 说「在携程上帮我找一个类似的双人团行程」:

图|Google

重点不在于 Gemini 能够执行什么任务,而是它拥有了更强大的「多做一步」的能力,有时候就是多的这一步,让 AI 从「能用」变成了「有用」。

当然 Android 17 的更新也不是 Gemini 的独角戏,谷歌同样对很多「Android 核心体验」进行了优化。

在 Android 17 中,谷歌和 Meta 达成了合作,在 Facebook、Instagram 等等 app 里支持了调用原生相机功能,比如 Ultra HDR、超级防抖、夜景视频等等。

而谷歌使用了好多年的平面风格 emoji 也迎来了一次更新——从原本的纯 2D 变成了 2.5D,在风格上更接近 iOS 使用的 emoji 了:

图|Google

而我们此前介绍过的 QuickShare 兼容 AirDrop 的功能,也将在 Android 17 上支持更多厂商的设备。

除了三星和 Pixel 之外,(国际版)OPPO、Vivo、一加和荣耀的较新机型也将在今年下半年陆续更新兼容 AirDrop 的固件:

图|Google

谷歌的 AI PC

在 Gemini Intelligence 之外,谷歌也没有忘记给这些更复杂、更强大的 AI 功能打造一套量身定制的硬件。

这个新硬件的形态,既不是吊坠,也不是耳机,更不是手表手环——而是曾经的 Chromebook。

没错,在 AI 时代,谷歌又双叒给自己的笔记本改名了。

从 Pixelbook,到 Chromebook,再到最新的 Googlebook:

图|Google

和 Chromebook 一样,Googlebook 并不是某一款具体的笔记本电脑,而是同样和第三方厂商合作、只要符合标准的都可以叫这个名字。

而 Googlebook,就是「第一款为 Gemini Intelligence 量身打造」的硬件产品。

除了上面的全新 Gemini Intelligence 功能之外,Googlebook 在日常使用最频繁的基础人机交互层面,做出了堪称革命性的创新——

在 Googlebook 上呼出 Gemini 功能,既不需要说话、也不需要按键、更不是右键菜单,只需要「摇一摇光标」就行。

图|Google

基于 Gemini Intelligence 的多模态能力,推荐的 AI 指令甚至可以根据光标下面的内容、选中的内容、屏幕上可以进行的操作等等因素自动调整。

在如今电脑端 AI 功能越来越密集、笔记本键盘空间不够充裕的情况下,Googlebook 的「魔法指针」无疑是最直观且优雅的解决方案之一。

此外,Googlebook 还解决了 ChromeOS 历史上的老大难问题:它是谷歌的产品,却跑不了 Android app。

换句话说,所有 Android 手机里面的 app,在 Googlebook 上都可以直接运行,基本看齐了如今 macOS 跑 iOS 软件的水平。

这一切的基础,就是爱范儿之前文章中提到的谷歌大力推行的 GKI(通用内核镜像)计划,正在让 Android 脱离手机的桎梏、无缝衔接到更多形态的设备上。

虽然谷歌目前没有提到这个功能的兼容情况,但我们猜测,依据处理器规格和网络状态,Googlebook 应该同时支持本地运行和画面投屏手机 app 两种方式。

图|Google

根据活动消息,首批 Googlebook 的生产厂商还是那几个熟悉的身影:宏碁、华硕、戴尔、惠普、联想等等,首批产品预计在今年内上市。

图|Google

除了笔记本电脑,Android 17 同样更新了一部分 Android Auto 车机系统的功能。

比如更像 OpenClaw 能力的 Gemini Intelligence 代操作、优化的 3D 道路画面、更加智能化的流媒体播放功能等等。

当然也支持把手机上自创的自定义 widget 显示在车机上。

图|Google

同时,原生支持 Android Auto 的品牌范围也在增加,部分型号甚至支持记忆当前车辆信息,类似后备箱尺寸、仪表盘规格等等。

这样一来,用户在使用 Gemini 问答的时候,车机就能给出具体回答,比如「能不能同时放俩 27 寸旅行箱?」或者「那个像是刺客的警示灯是什么意思」之类的。

可惜的是,这项功能目前也不会立即上线,同样预计为「今年晚些时候」才会有产品搭载。

总的来说,本次活动只是今年 Google I/O 的开胃菜,但它涉及到的理念变革却是非常根本性的——

其实在活动的开头,谷歌就指出了:好用的人工智能技术,就应该是让人感受不到的,它会融入进每一层软件和硬件的体验。

图|Google

而这正是 Gemini Intelligence 在做的。

无论是 Pixel 手机、Android Auto 车机还是 Googlebook,这些硬件最终都只是 Gemini 智能的一种体现方式而已。

值得玩味的是——谷歌今天晚上所做的,刚好就是苹果削尖脑袋想要实现的那套 AI 生态。

让 iPhone、手表和 Mac 共用一套智能体系,用户无论在哪里使用,功能和体验都是高度相似的,硬件只区分交互方式、不影响智能水平。

图|Apple

可惜的是,苹果挣扎了这么久,也没有搞定「模型」的部分,反而让自己的硬件成了别家模型的嫁衣。

将来的智能系统(Intelligence System),形式比现在更多样、但核心却比现在更加统一。

#欢迎关注爱范儿官方微信公众号:爱范儿(微信号:ifanr),更多精彩内容第一时间为您奉上。

Android Studio 新版本与 Gradle 7.x 构建报错解决方案

作者 wyanassert
2026年5月11日 17:39

问题现象

在 Android Studio 中 Build / Sync 报错:

1
2
3
4
5
6
7
8
Could not compile initialization script '.../ijMapper1.gradle'.
> startup failed:
General error during conversion: Unsupported class file major version 65

java.lang.IllegalArgumentException: Unsupported class file major version 65
at groovyjarjarasm.asm.ClassReader.<init>(ClassReader.java:199)
...
at com.intellij.gradle.toolingExtension.impl.modelAction.GradleModelFetchAction...

从命令行直接执行 ./gradlew tasks 完全正常,只有通过 Android Studio 触发构建时才报错。


根本原因

这是一个 Android Studio 版本 × Gradle 版本 的二进制不兼容问题,与项目代码无关。

层级 说明
class file major version 65 Java 21 编译产物的标识(Java 11 = 55,Java 17 = 61,Java 21 = 65)
Android Studio Koala(2024.1)及更新版本 自带 JDK 21,其 Tooling Extension JARs(如 GradleOpenTelemetryGradleModelFetchAction)使用 Java 21 编译
Gradle 7.4.2 内置 Groovy 3.0.9 / ASM 9.1 ASM 9.1 最高只能解析 Java 17(class version 61)的 class 文件,无法读取 Java 21 产物

Android Studio 每次构建会向 Gradle 注入一个临时 init script(ijMapper1.gradle),Groovy 编译这个脚本时需要解析 classpath 上的 Android Studio Tooling JARs。新版 AS 的这些 JAR 是 Java 21 编译的,Gradle 7.x 内置的 ASM 读不了,直接崩溃。

命令行不受影响的原因:直接执行 ./gradlew 不经过 Android Studio 的 Tooling API,不会注入这个 init script。


解决方案

✅ 方案一:在 Android Studio 中指定 Gradle JDK 为 JDK 11(本文实际采用,推荐)

修改项目的 .idea/gradle.xml,将 gradleJvm 改为 JDK 11 的路径(路径需已在 Android Studio 的 JDK Table 中注册):

1
2
<option name="gradleJvm"
value="$USER_HOME$/Library/Java/JavaVirtualMachines/corretto-11.0.19/Contents/Home" />

注意:这里必须使用 $USER_HOME$ 变量形式,而非绝对路径,否则 Android Studio 在 jdk.table.xml 中匹配不到对应条目,报 “Undefined jdk.table.xml entry” 错误。

配置完成后,Android Studio 会:

  1. 用 JDK 11 启动 Gradle daemon
  2. 选用与 JDK 11 兼容的 Tooling Extension JARs(class version ≤ 61),避免 ASM 解析失败

此方案不影响其他同事(.idea/gradle.xml 中的路径是各自机器上注册过的条目,不同机器只要都注册了 JDK 11 即可)。


方案二:升级 Gradle 到 8.4+(需团队协调)

Gradle 8.x 采用 Groovy 4.x / ASM 9.5+,原生支持 Java 21 class 文件,从根本上消除兼容问题。

1
2
# gradle/wrapper/gradle-wrapper.properties
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-all.zip

代价:AGP 7.x 不兼容 Gradle 8.x,需同步将 AGP 升级到 8.x,改动较大,需要团队统一升级。


方案三:降级 Android Studio(临时方案)

降回 Android Studio Jellyfish(2023.3.1) 或更早版本,其 Tooling Extension JARs 基于 Java 17 编译(class version 61),Gradle 7.x 的 ASM 9.1 可以正常解析。


排查思路总结

遇到此类问题时,快速定位的关键线索:

  1. 只在 IDE 内报错,命令行正常 → 问题出在 IDE 注入的 init script,而非项目构建脚本本身
  2. Unsupported class file major version 65 → 某个依赖/工具 JAR 用 Java 21 编译,运行时的 ASM/JVM 版本不支持
  3. 错误堆栈含 com.intellij.gradle.toolingExtension → 确认是 Android Studio Tooling Extension 和 Gradle 的版本兼容性问题

兼容性速查表

Android Studio 版本 内置 JDK Tooling JAR class version 最低兼容 Gradle
Jellyfish 2023.3.1 及以前 JDK 17 61(Java 17) Gradle 7.x 可用
Koala 2024.1.1 及以后 JDK 21 65(Java 21) 需 Gradle 8.4+ 或手动指定 Gradle JDK 为 17/11

AI 硬件时代,手机不会轻易的狗带|AI 器物志

作者 马扶摇
2026年5月8日 18:00

智能手机统治了过去十几年的数字生态,它是注意力的黑洞,是我们最私密的随身之物。但手机从设计之初就是为「人盯着它」而生的——它的全部逻辑,都止于屏幕。

AI 的需求却恰恰相反:它需要持续感知物理世界——见你所见,听你所闻,随时在场,而非等你解锁屏幕才醒来。

当 AI 真正成为一种基础能力,它迟早要从屏幕里破壳而出,寻找属于它自己的形状。这将是一个漫长的探索和演化过程。

「AI 器物志」栏目由此而来,爱范儿想和你一起持续观察:AI 如何改变硬件设计,如何重塑人机交互,以及更重要的——AI 将以怎样的形态进入我们的日常生活?

这是「AI 器物志」的第 11 篇文章。

说起来,你有多久没有尝试把手机接到显示器上了?

在曾经那个硬件形态百花齐放的「前智能机」时代,我们总希望能够从里面榨取出更多的使用场景——

无论是展开变成 QWERTY 键盘,还是滑盖变成一台小电脑:

如今智能手机的形态虽然变得单调,但我们实际上从来都没有忘记「把手机变成点儿别的什么形态」这件事。

安卓电脑

在最新的 Android 17 Beta 3 中,谷歌就上线了那个传闻已久的「桌面模式」,允许用户将 Pixel 手机外接显示器之后,投屏出一个近似 PC 布局的桌面:

此时再接上蓝牙鼠标和键盘,那么这台 Pixel 9 Pro 在功能性上已经非常接近一台小电脑了,顺便还能运行 Android 应用:

实际上,对于谷歌来说,Android 17 桌面模式其实有个更浅显易懂的名字:ChromeOS 模式。

毕竟两者都是类 Unix 架构,在谷歌推动 GKI 通用内核(Generic Kernal Image)的大背景下,说 Android 正在与 ChromeOS 融合也不为过。

只不过 Android 17 Beta 3 的桌面模式还不是完全体,无论是在功能性还是流畅度上都还有许多优化的空间。

Android 17 中完全没有为显示器优化的通知/控制中心

相比快十岁的三星 DeX 来说,Pixel 桌面模式在分辨率调整、布局逻辑、交互设计等等方面还有很多不成熟的地方,现阶段只能算是「勉强能用」。

DeX 已经支持最高 4K 分辨率和类 Windows UI

考虑到谷歌近两年推送新功能的速度,这套桌面模式在 Android 17 生命周期内可能不会成为正式上架

或许在今年 5 月的 Google I/O 大会、以及后续 Android 18 beta 上,我们才会见到一个完成度接近 DeX 的版本。

谷歌去年展示的桌面模式 demo|Google

不过 Android 17 Beta 3 虽然不行,但「将手机拓展到其他交互媒介」思路本身却是非常有前景的。

这种「以一变多」的逻辑,在面对如今的专用 AI 硬件时,显得尤为发光发热。

数字中枢

设想这样一个场景——

你拿着一台手机,连接桌子上的显示器,它就显示一个功能完整的类 PC 桌面,所有的交互和操作都可以通过键鼠完成。

转过头来,把手机从显示器上拔下,通过无线串流的方式连接到 AI 眼镜,刚刚显示的 PC 模式就丝滑地切换成了 XR 模式:

Android XR 演示 UI|Android Authority

虽然目前无论是最新的 Android 17 与 Pixel 10 Pro,还是尚未开售的 Android XR,都暂时无法实现这样的使用场景。

但这种「基于手机串流」的未来,距离我们并不远。

即便对 Vision Pro 这样自带处理器的产品来说,串流依然是不可或缺的使用场景,尤其是作为 Mac 的拓展显示器:

图|UploadVR

前些天,Valve 正式为 visionOS 带来了官方的 Steam Link app,让 Vision Pro 直连 PC 玩游戏不再需要折腾第三方串流客户端了。

从 TestFlight 版本的体验来看,Vision Pro 版本的 Steam Link 体验在普通的家用网络环境下几乎已经可以做到零延迟串流。

甚至对于穿越机模拟器《Liftoff》之类的超快节奏的游戏也能胜任:

图|YouTube @Himels Tech

背后的原因很简单:无论软件生态、交互模式,还是基础性能和续航,将新硬件与现有设备串流,永远比单独开发新的 AI 系统、软件和交互要便捷省时许多。

这个串流的来源既然可以是电脑,那为什么不能是手机呢?毕竟连 A18 Pro 都可以运行完整版 macOS 了。

同理,这种「将运算的重担交给手机」的串流模式除了放在 AR、XR 头显上,自然也可以平移到各种形态的 AI 新硬件上——

事实上,现在的绝大多数 AI 硬件就是这么做的。

图|Heise

近有 PLAUD Note 录音卡,远有 Meta Ray-Ban 智能眼镜,虽然都是独立的产品,但运行逻辑却离不开手机——

AI 硬件自身仅作为采集信息和用户交互的媒介,但运算、AI、归档等等,其实都是在手机和云端完成的。

All in one

另一方面,除了运算逻辑之外,现阶段我们能够看到或者买到的所有 AI 硬件,无论眼镜、耳机、戒指、吊坠,都面临着一个绕不开的问题:

人类电池科技被智子封锁,所有这些小物件的续航都难以突破。

图|Android Central

这和我们期盼的「AI 硬件能够成为全天候服务我们个人生活」的初衷是相违背的,在某种程度上也促成了如今这个硬件市场不温不火的现状。

纵使硬件或者 AI 功能再强大,「不能像手机一样持久、普适、全能」,就是会非常影响人们的使用意愿。

图|Museum of failure

然而手机没有这种问题。

一方面来说,我们已经习惯了手机的使用节奏,从早上起床到凌晨上床,顶多充一会电,基本不会出现某些可穿戴 AI 硬件那种「突然暴毙」的情况。

另一方面则更根本一些——

手机在本地算力、软件生态、内容容量方面,都是早已建成的「基建」,AI 硬件将自己化身为手机的延伸,最显著的优点就是可以省下重复发明轮子的麻烦。

摩托罗拉 AI 吊坠,需要搭配 moto 手机使用|Times of AI

说得更直白一些:要想富,先修路。

手机作为目前最全能、最普世的随身智能设备,就是那条已经修好的「路」。

而 AI 硬件要想得到消费群体的接受,就必须先沿着这条路走出来,然后才有资格谈「如何革手机的命」。

在庆祝公司成立 50 周年前夕,苹果全球营销高级副总裁格雷格·乔斯维亚克(Greg Joswiak)和硬件工程高级副总裁约翰·特努斯(John Ternus)接受一次了《连线》杂志的采访。

格雷格(右)与约翰(左)|Tom’s Guide

而在谈到「苹果是否希望现在市面上各种 AI 新硬件中有苹果的身影」问题时,格雷格·乔斯维亚克这样回答道:

我们不能忽视一个事实,即你刚才所说的一切都与 iPhone 并不冲突,iPhone 不会消失。在刚刚谈论的(为 AI 设计一种专门硬件)任何事情中,iPhone 都将扮演核心角色。

换言之,苹果认为——即使在 AI 专用硬件的市场出现之后,人们依然会选择 iPhone 作为个人智能设备的中枢。

对于这个问题,格雷格补充道:

这正是其他所有人(厂商)挣扎的地方:它们没有 iPhone,所以它们正在拼命寻找出路。(AI 硬件厂商)谈论的很多东西最后都成了 iPhone 的配件,我们不会透露未来的路线图,但我可以告诉你,iPhone 哪里都不会去。

而在《连线》杂志后续采访蒂姆·库克有关苹果未来 50 年的计划时,在谈及苹果的人才、价值观和文化之后,库克也给出了一个相似的回答:

是的,未来的技术会改变;是的,会有更多的产品和品类,这些都是事实。但让苹果成为苹果的东西,在未来的 50 年、100 年甚至 1000 年里都会是一样的。

这里说的当然不是 iPhone,而是苹果设备作为人们的数据数字中枢这件事情本身。

图|Chad Madden

无论 OpenAI 或者其他 AI 厂商做出什么「AI 专用产品」,哪怕是在 AI 与硬件形态深度融合的时代,手机依然会是我们日常生活中不可或缺的数据中枢,就像二十年前的 PC 一样。

我们甚至可以说,随着云端模型越来越强大、侧端数据越来越复杂,手机作为能够连接外设、人体、与云端的「桥梁」,重要性只会越来越高。

未来的 AI 硬件不一定长得像手机,但它的灵魂一定离不开手机。

#欢迎关注爱范儿官方微信公众号:爱范儿(微信号:ifanr),更多精彩内容第一时间为您奉上。

状态管理大乱斗#06 | Riverpod 源码评析 (下) - 外功心法

2026年5月6日 08:16

aeb07d6daf42f480cdfcd33f7b87ab55.png

引言:

前两篇我们拆解了 Riverpod 的核心架构和类型系统。那些是"内功"。这一篇聊"外功"——Riverpod 怎么和 Flutter 的 Widget 树连接起来,以及在实战中有哪些值得掌握的技巧。

Riverpod 的状态管理系统是独立于 Widget 树的,但最终状态要驱动 UI 更新。这个"桥梁"怎么搭的?搭得好不好?看完源码你就知道了。


一、ProviderScope:桥梁的桥墩

ProviderScope 是 Riverpod 和 Flutter 之间的桥梁。每个 Flutter 应用的根部都要包一个 ProviderScope,它的作用是把 ProviderContainer 注入到 Widget 树中。


1. ProviderScope 的本质
---->[packages/flutter_riverpod/lib/src/core/provider_scope.dart#ProviderScope]----
final class ProviderScope extends StatefulWidget {
  const ProviderScope({
    super.key,
    this.overrides = const [],
    this.observers,
    this.retry,
    required this.child,
  });

  final List<Override> overrides;
  final List<ProviderObserver>? observers;
  final Widget child;
}

ProviderScope 本身是一个 StatefulWidget。它在 initState 中创建 ProviderContainer,在 dispose 中销毁它:

---->[packages/flutter_riverpod/lib/src/core/provider_scope.dart#ProviderScopeState]----
class ProviderScopeState extends State<ProviderScope> {
  late final ProviderContainer container;

  @override
  void initState() {
    super.initState();
    final parent = _getParent();  // tag1: 查找父 ProviderScope

    container = ProviderContainer(
      parent: parent,              // tag2: 建立容器树
      overrides: widget.overrides,
      observers: widget.observers,
    );
  }

  @override
  void dispose() {
    container.dispose();  // tag3: Widget 销毁时,容器也销毁
    super.dispose();
  }
}

tag1 处通过 context.getElementForInheritedWidgetOfExactType 查找父级的 ProviderScope。如果找到了,新容器以它为 parent(tag2)。tag3 处 Widget 销毁时容器也销毁——生命周期和 Widget 树绑定。


2. _UncontrolledProviderScope:真正的 InheritedWidget

ProviderScopebuild 方法返回的是一个 UncontrolledProviderScope,它内部包了一个 _UncontrolledProviderScope——这才是真正的 InheritedWidget

---->[packages/flutter_riverpod/lib/src/core/provider_scope.dart#_UncontrolledProviderScope]----
final class _UncontrolledProviderScope extends InheritedWidget {
  const _UncontrolledProviderScope({
    required this.container,
    required super.child,
  });

  final ProviderContainer container;

  @override
  bool updateShouldNotify(_UncontrolledProviderScope oldWidget) {
    return container != oldWidget.container;  // tag4: 容器变了才通知
  }
}

tag4 处的 updateShouldNotify 只在容器实例变化时返回 true。容器实例在 ProviderScope 的生命周期内不会变,所以这个 InheritedWidget 几乎不会触发子树重建。

停下来想想:如果 updateShouldNotify 总是返回 false,那 Consumer 是怎么知道 Provider 的值变了的?

答案:Consumer 不是通过 InheritedWidget 的通知机制来感知 Provider 变化的。它是通过 ProviderSubscription 直接订阅 Provider,Provider 变化时通过订阅回调触发 setState。InheritedWidget 只是用来传递 ProviderContainer 的引用,不负责状态变化的通知。

这是一个很聪明的设计:用 InheritedWidget 做"容器的传递"(低频),用 Subscription 做"状态的通知"(高频)。两个机制各司其职。


3. vsync 同步:和 Flutter 帧对齐

_UncontrolledProviderScopeState 中有一段关键代码:

---->[packages/flutter_riverpod/lib/src/core/provider_scope.dart#_UncontrolledProviderScopeState]----
@override
void initState() {
  super.initState();
  widget.container.scheduler.flutterVsyncs.add(_flutterVsync); // tag5: 注册帧同步
}

void _flutterVsync(Task task) {
  _task = task;
  _vsyncTimer = Timer(Duration.zero, () {
    if (mounted) setState(() {});  // tag6: 触发 Widget 重建

    _vsyncTimOutTimer = Timer(Duration.zero, () {
      _callTask();  // tag7: 执行调度任务
    });
  });
}

@override
Widget build(BuildContext context) {
  _callTask();  // tag8: build 时执行待处理的任务
  // ...
}

tag5 处把 _flutterVsync 注册到调度器中。当有 Provider 需要刷新时,调度器调用 _flutterVsync,它通过 setStatetag6)触发 Widget 重建。在 build 方法中(tag8),待处理的任务被执行,Provider 的值被刷新。

这个机制保证了 Provider 的刷新和 Flutter 的帧渲染是同步的——Provider 在 Widget build 之前完成刷新,Widget 读到的永远是最新值。


二、ConsumerWidget:水龙头

ConsumerWidget 是用户接触最多的 API。它让 Widget 能够读取 Provider 的值,并在值变化时自动重建。


1. WidgetRef 的设计
---->[packages/flutter_riverpod/lib/src/core/widget_ref.dart#WidgetRef]----
sealed class WidgetRef implements MutationTarget {
  BuildContext get context;

  StateT watch<StateT>(ProviderListenable<StateT> provider);
  StateT read<StateT>(ProviderListenable<StateT> provider);
  void listen<StateT>(ProviderListenable<StateT> provider, /* ... */);
  ProviderSubscription<StateT> listenManual<StateT>(/* ... */);
  StateT refresh<StateT>(Refreshable<StateT> provider);
  void invalidate(ProviderOrFamily provider);
}

WidgetRef 是一个 sealed class,和 Ref 类似但面向 Widget 层。它的 API 和 Ref 几乎一样:watchreadlisten。区别在于 WidgetRef 多了一个 context 属性,以及 listenManual 方法。

为什么要分 RefWidgetRef 两个接口?因为它们的使用场景不同:

  • Ref 在 Provider 的 build 函数中使用,生命周期和 Provider 绑定
  • WidgetRef 在 Widget 的 build 方法中使用,生命周期和 Widget 绑定

分开之后,编译器能帮你检查:你不会在 Widget 层误用 ref.invalidateSelf()(那是 Provider 层的 API),也不会在 Provider 层误用 ref.context(那是 Widget 层的 API)。


2. Consumer 的 build 流程
---->[packages/flutter_riverpod/lib/src/core/consumer.dart#Consumer]----
final class Consumer extends ConsumerWidget {
  const Consumer({super.key, required this.builder, this.child});

  final ConsumerBuilder builder;
  final Widget? child;

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return builder(context, ref, child);
  }
}

Consumer 本身很简单,就是把 builder 函数包装成一个 ConsumerWidget。真正的魔法在 ConsumerStatefulElement 中——它在 build 时创建 WidgetRef,通过 WidgetRef.watch 建立订阅,Provider 变化时通过订阅回调触发 setState

整个链路:

sequenceDiagram
    participant CW as ConsumerWidget
    participant CSE as ConsumerStatefulElement
    participant WR as WidgetRef
    participant PC as ProviderContainer
    participant PE as ProviderElement
    participant Sched as Scheduler

    CW->>CSE: build(context)
    CSE->>WR: 创建 WidgetRef
    CW->>WR: ref.watch(counterProvider)
    WR->>PC: container.listen(counterProvider)
    PC->>PE: mount + build(如果未初始化)
    PE-->>WR: 返回当前值 + 创建 Subscription
    WR-->>CW: 返回值,Widget 构建完成

    Note over PE: counter 值变化
    PE->>Sched: scheduleProviderRefresh
    Sched->>CSE: _flutterVsync → setState
    CSE->>CW: 重新 build
    CW->>WR: ref.watch(counterProvider)
    WR->>PE: flush + read
    PE-->>WR: 返回新值

3. TickerMode 暂停优化

Riverpod 有一个很贴心的优化:当 Widget 不可见时(TickerMode.of(context) 为 false),自动暂停所有订阅。

这意味着:如果你有一个 Tab 页面,切到其他 Tab 时,当前 Tab 的 Provider 订阅会被暂停。Provider 不会被销毁(状态保留),但也不会触发不必要的重建。切回来时自动恢复。

这个优化对性能的影响在复杂应用中是很明显的。你不需要写任何代码,框架自动帮你做了。


三、实战心法:从源码中提炼的使用技巧

看完源码,很多"最佳实践"就不再是死记硬背的规则,而是有源码支撑的理解。


1. watch 放在 build 的最顶层
---->[✅ 正确做法]----
@override
Widget build(BuildContext context, WidgetRef ref) {
  final count = ref.watch(counterProvider);  // 顶层 watch
  final user = ref.watch(userProvider);      // 顶层 watch

  return Column(
    children: [
      Text('$count'),
      Text(user.name),
    ],
  );
}

为什么?因为 ref.watch 建立的订阅在每次 build 时会被重新创建(旧订阅被清理)。如果你把 watch 放在条件分支里,某些 build 可能不会执行到那个 watch,导致订阅丢失,下次值变化时不会触发重建。

从源码层面看,这和 Provider 的 _performBuild_runOnDispose 清理旧订阅是同一个机制。


2. 事件处理用 read,不用 watch
---->[✅ 正确做法]----
ElevatedButton(
  onPressed: () {
    ref.read(counterProvider.notifier).increment();  // 事件中用 read
  },
  child: Text('加一'),
)

read 不建立订阅,只是一次性读取。在事件处理中你不需要"监听变化",你只需要"拿到当前值然后操作"。用 watch 反而会建立不必要的订阅。


3. 副作用用 listen,不用 watch
---->[✅ 正确做法]----
@override
Widget build(BuildContext context, WidgetRef ref) {
  ref.listen(authProvider, (prev, next) {
    if (!next.isAuthenticated) {
      Navigator.of(context).pushReplacementNamed('/login');
    }
  });

  return /* ... */;
}

listen 只触发回调,不触发 Widget 重建。弹对话框、导航、显示 SnackBar 这些副作用,用 listenwatch 更合适。watch 会导致整个 Widget 重建,但你只是想执行一个副作用,不需要重建 UI。


4. select 优化重建粒度
---->[✅ 优化前]----
// 用户的任何字段变化都会触发重建
final user = ref.watch(userProvider);
return Text(user.name);

---->[✅ 优化后]----
// 只有 name 变化才触发重建
final name = ref.watch(userProvider.select((u) => u.name));
return Text(name);

从源码层面看,select 创建了一个 _ProviderSelector,它在原始 Provider 变化时先执行 selector 函数,然后用 == 比较新旧结果。只有结果不同才通知 Widget。

在列表页面中,这个优化的效果很明显。如果你 watch 了一个包含 100 个 todo 的列表,任何一个 todo 的变化都会导致整个列表重建。用 select 可以让每个 todo item 只在自己的数据变化时重建。


5. autoDispose + keepAlive 的组合拳
---->[示例代码]----
final searchResultProvider = FutureProvider.autoDispose
    .family<List<Item>, String>((ref, query) async {
  // 数据加载完成后,保持缓存
  final link = ref.keepAlive();

  // 30 秒后允许销毁
  final timer = Timer(Duration(seconds: 30), link.close);
  ref.onDispose(timer.cancel);

  return api.search(query);
});

这个模式实现了"带过期时间的缓存":数据加载完成后通过 keepAlive 阻止销毁,30 秒后释放 link 允许销毁。如果 30 秒内用户再次访问,直接使用缓存;超过 30 秒,下次访问时重新加载。

从源码层面看,keepAlive_keepAliveLinks 列表里加了一个 link,_performDispose 检查这个列表是否为空来决定是否销毁。link.close 从列表中移除 link,如果列表空了就调用 mayNeedDispose


6. Override 做依赖注入
---->[示例代码]----
// 定义抽象接口
final httpClientProvider = Provider<HttpClient>((ref) {
  return DioHttpClient();  // 默认实现
});

// 测试中替换
ProviderScope(
  overrides: [
    httpClientProvider.overrideWithValue(MockHttpClient()),
  ],
  child: MyApp(),
)

这比 GetX 的 Get.put 更安全:override 的作用域是明确的(只影响当前 ProviderScope 及其子树),不会污染全局状态。测试之间互不影响。


7. 用 Provider 做派生状态
---->[示例代码]----
final todosProvider = NotifierProvider<TodoList, List<Todo>>(TodoList.new);

final completedTodosProvider = Provider<List<Todo>>((ref) {
  final todos = ref.watch(todosProvider);
  return todos.where((t) => t.isCompleted).toList();
});

final incompleteTodosProvider = Provider<List<Todo>>((ref) {
  final todos = ref.watch(todosProvider);
  return todos.where((t) => !t.isCompleted).toList();
});

completedTodosProviderincompleteTodosProvider 是从 todosProvider 派生出来的。todosProvider 变化时,两个派生 Provider 自动重新计算。如果计算结果没变(比如你修改了一个已完成的 todo 的标题),依赖它们的 Widget 不会重建。

这是函数式 Provider 最典型的用法:把"计算逻辑"从 Widget 层提取到 Provider 层,让框架帮你管理缓存和更新。


四、终极对比:四大方案的源码级总结

四篇文章写下来,是时候做一个完整的对比了。这不是"哪个最好"的排名,而是从源码层面看它们各自的设计选择和代价。


graph TD
    subgraph "设计哲学"
        G["① GetX<br/>快意江湖<br/>全局字典"]
        B["② Bloc<br/>大道至简<br/>事件驱动状态机"]
        P["③ Provider<br/>顺水行舟<br/>封装 InheritedWidget"]
        R["④ Riverpod<br/>源远流长<br/>独立容器树"]
    end

    subgraph "和 Flutter 的关系"
        G --> GF["绕过框架<br/>全局变量"]
        B --> BF["桥接集成<br/>用 provider 包桥接"]
        P --> PF["深度集成<br/>用框架的机制"]
        R --> RF["平行系统<br/>自己的容器树"]
    end

    style G fill:#fdf,stroke:#333
    style B fill:#ffd,stroke:#333
    style P fill:#9f9,stroke:#333
    style R fill:#dff,stroke:#333
维度 GetX Bloc Provider Riverpod
底层机制 全局静态 Map Stream + provider 包 InheritedWidget 独立容器树
状态存储 全局字典 Bloc 实例(Widget 树上) Widget 树上 ProviderContainer
依赖追踪 隐式 proxy,运行时收集 无内置(手动监听 Stream) 显式 of(context) 显式 ref.watch
作用域 无,全局唯一 Widget 树天然支持 Widget 树天然支持 ProviderScope 嵌套覆盖
精准重建 无(Obx 整体重建) BlocSelector / buildWhen context.select ref.watch + select
生命周期 SmartManagement(路由绑定) 和 Widget 绑定 和 Widget 绑定 autoDispose + keepAlive + pause/resume
可追溯性 Transition 记录事件+状态 无内置
并发控制 EventTransformer 四种策略 无内置
异步支持 无内置 自定义状态类 FutureProvider(有限) AsyncValue(完整)
测试 手动 Get.put bloc_test 包 需要 Widget 环境 Override 替换,纯 Dart
依赖 context ❌ 全局访问 ✅ 通过 provider ✅ 必须 ❌ Ref 独立
脱离 Flutter ✅ bloc 核心包纯 Dart ✅ 纯 Dart 可用
DevTools 不可见 Widget Inspector 可见 Widget Inspector 可见 专用 DevTools
源码量 ~数千行 ~500 行 ~1000 行 ~数千行
学习曲线 中-高

四条路,四种哲学

GetX 像路边摊——什么都能做,灵活但没规矩。全局字典一把梭,快是快,但项目大了容易失控。

Bloc 像标准化连锁店——流程固定、品控稳定、可复制性强。事件驱动的状态机让每一次状态变更都有迹可循,但样板代码是实实在在的成本。

Provider 像自家厨房——用的是家里现成的锅碗瓢盆(InheritedWidget),不用额外添置设备。学了 Provider 就是在学 Flutter 本身,但厨房的大小受限于房子(context)。

Riverpod 像米其林餐厅——食材供应链精密复杂,出品质量高,但运营成本也高。独立容器树、autoDispose、AsyncValue、Override——能力边界最广,但学习曲线也最陡。

怎么选
  • 刚入门 Flutter,项目不大 → Provider 或 Cubit。贴近框架,学习成本低。
  • 中等规模,需要可追溯性和并发控制 → Bloc。事件系统和 BlocObserver 在团队协作中很有价值。
  • 大型项目,需要复杂的依赖管理和测试 → Riverpod。容器树、autoDispose、Override 在复杂场景下优势明显。
  • 快速原型,不在乎架构 → GetX 或 Cubit。但要做好后期迁移的心理准备。

没有最好的方案,只有最适合当前阶段的方案。


五、Riverpod 的天花板在哪

公道地说,Riverpod 也不是完美的。


1. 概念负担

Provider、NotifierProvider、FutureProvider、StreamProvider、Family、autoDispose、select、Override、ProviderScope、Ref、WidgetRef……概念确实多。对于一个只想"把数据从 A 传到 B"的新手来说,这个学习成本是实实在在的。


2. 代码生成的依赖

Riverpod 2.0+ 推荐使用 @riverpod 注解 + 代码生成。这简化了 Provider 的定义,但也引入了对 build_runner 的依赖。代码生成在大型项目中的编译速度是一个痛点。


3. 调试的间接性

状态不在 Widget 树上,Widget Inspector 看不到。虽然有 Riverpod DevTools,但它是一个独立的工具,不如 Widget Inspector 那样和 IDE 深度集成。


4. 过度设计的风险

Riverpod 的能力很强,但也容易过度设计。一个简单的计数器应用,用 setState 三行代码搞定的事,用 Riverpod 可能要定义 Provider、Notifier、ProviderScope……杀鸡用牛刀。

适合的时期,学适合的东西,也是非常重要的。如果你的项目还在原型阶段,不需要作用域隔离、不需要精准重建、不需要复杂的测试,那 Riverpod 的很多能力你用不上。等项目长大了再引入也不迟。


碎碎念

四篇文章(GetX → Bloc → Provider → Riverpod)写下来,最大的感受是:它们解决的是同一个问题,但走的是完全不同的路。

GetX 用一本全局字典解决一切,简单粗暴,快意江湖。Bloc 用事件驱动的状态机,严谨可控,大道至简。Provider 顺着 Flutter 的水流走,用框架自己的 InheritedWidget,顺水行舟。Riverpod 在 Flutter 旁边挖了一条新河,独立容器树,源远流长。

四条路都能到达目的地。选哪条,取决于你的项目有多大、团队有多少人、你愿意付出多少学习成本。

从源码质量来看,四个方案都有值得学习的地方:GetX 的 proxy + save/restore 自动依赖收集确实精巧;Bloc 的接口隔离和 EventTransformer 策略模式是教科书级设计;Provider 的 Delegate 模式和 aspect 精准通知把 InheritedWidget 的能力发挥到了极致;Riverpod 的容器树、调度器、生命周期管理(pause/resume/dispose)是工程质量最高的实现。

但我也理解为什么有人觉得"选择太多了"。不是每个项目都需要容器树、事件追溯、精准重建。就像不是每个人都需要一辆越野车——如果你只在城市里开,一辆轿车就够了。但如果你要去越野,轿车就不行了。关键是知道自己要去哪里。

说到底,技术选型是一个权衡。了解了源码之后,这个权衡你自己就能做了。不需要听别人说"XX 好"或者"XX 不好"——自己去看源码,自己去验证,自己去判断。

人云亦云是技术成长最大的敌人。


我是张风捷特烈,如果你对 Flutter 框架的源码分析感兴趣,欢迎关注。「状态管理大乱斗」系列到这里来到第六篇,后续还会有其他状态管理分析,敬请期待。GetX 的全局字典、Bloc 的事件状态机、Provider 的 InheritedWidget 封装、Riverpod 的独立容器树——四条路,四种哲学,希望对你有帮助。

Swift 杀进 Android,Google 和 Apple 都要失眠了?

作者 黄林晴
2026年3月31日 08:49

本文同步自微信公众号 “Android技术圈”

Swift 终于正式杀进 Android 了。 不是社区 Demo,不是民间魔改,而是 Swift 官方第一次亲自发布 Android SDK。 这件事现在看起来像一条技术新闻,再往后看,很可能会变成移动开发格局变化的起点。

先别急着喊 “Kotlin 要完了”。 但也别把它轻飘飘理解成 “Swift 又做了个跨平台实验”。 因为这次最狠的地方在于:Swift 官方第一次给出了进入 Android 工程体系的真实路径。

cover.png

官方到底宣布了什么

比“Apple 要不要抢 Android 地盘”更值得看的,其实是官方这次到底把支持范围推进到了哪一步。

Swift 官方博客对这件事的表述,其实非常克制,但也非常明确。核心就三点:

  • Swift 6.3 包含首个官方 Android Swift SDK
  • 可以开始用 Swift 开发原生 Android 程序
  • 可以通过 Swift JavaSwift Java JNI Core,把 Swift 代码集成进现有 Kotlin / Java Android 应用

这三句话加起来,真正说明的是:

Swift on Android,已经从社区探索,进入官方支持阶段。

这一步为什么重要?

因为过去你看到的很多 “Swift 跑 Android”,本质上都还是社区项目、实验性方案,或者少数团队自己打补丁维护的链路。

而这次不一样。 这次是 Swift 官方把 SDK、文档、安装方式、交叉编译路径和互操作方案一起摆上台面。

对开发者来说,差别非常大。

社区方案是“能折腾出来”。 官方方案才是“值得认真观察是否能进生产”。

02_ecosystem_bridge.png

别高估,也别低估

这类新闻最容易出现两个极端判断。

第一种是:
“Kotlin 要凉了,Android 以后可以全用 Swift 写。”

第二种是:
“没意义,不就是把命令行程序编到 Android 上吗?”

这两种都不准确。

从 Swift 官方文档来看,今天已经明确成立的是:

  • 你可以把 Swift 代码交叉编译到 Android
  • 你可以在 Android 设备或模拟器上运行 Swift 程序
  • 你可以把 Swift 模块构建成共享库
  • 你可以让现有 Kotlin / Java Android 应用去调用这些 Swift 代码

这意味着什么?

意味着 Swift 现在已经不是“只能在 Apple 生态里玩”的语言了。 但它也还远远没到“Android 主流团队明天集体切语言”的程度。

它真正有价值的地方,不是在口号层。 而是在工程层。

它第一次让 Swift 有机会进入 Android 的真实项目结构里。

真正值得关注的,是这条工程路径

Swift 官方的 Android 入门文档其实很务实,没有画大饼。

要跑起来,你得准备 3 样东西:

  • Swift 6.3 toolchain
  • Swift SDK for Android
  • Android NDK 27d 或更高版本

然后通过 swift build --swift-sdk ... 做 Android 目标的交叉编译。

官方演示的第一步,也不是完整 App,而是先把一个 Swift 可执行程序编译到 Android 上运行。

很多人看到这里会下意识地说:

“那不还是离真正 Android App 很远?”

但官方文档后面紧接着补了一句特别关键的话:

Swift 模块可以构建为共享库,并被打进 Android 应用,再由 Java / Kotlin 代码调用。

这句话,才是整件事的核心。

因为这说明 Swift on Android 的第一落点,并不是 UI 层,也不是整项目重写。 而是更现实、更符合团队采纳路径的地方:

  • 共享业务逻辑
  • 数据模型
  • 网络层
  • 算法模块
  • 某些性能敏感代码

说白了,Swift 现在不是先来抢 Compose 的饭碗。 它更像是先从“共享模块语言”这个位置切进去。

03_shared_logic_path.png

这件事为什么会让很多 iOS 团队心动

对纯 Android 团队来说,这条新闻现在更像观察项。 但对已经重度使用 Swift 的团队,这个信号就完全不一样了。

过去很多团队都会面临一个老问题:

“iOS 侧已经有一套成熟的 Swift 代码资产了,Android 还要不要重写一遍?”

如果这些资产只是 UI,那没办法,平台差异太大。 但如果是业务规则、网络协议、加解密逻辑、通用数据处理,重写本身就是重复劳动。

Swift 官方 Android SDK 的出现,至少让这类团队开始有了一个新的选项:

把 Swift 资产往 Android 端延伸,而不是永远困在 Apple 生态内部。

这点其实很关键。

因为它改变的不是某个 API,而是开发者对 Swift 的心理预期。

过去大家默认 Swift 是: “写 iPhone、写 Mac、写 Apple 设备。”

现在这个认知边界开始松了。

Swift 正在从 Apple 平台语言,继续往真正的跨平台语言走。

Kotlin 会不会被威胁

短期答案很直接:不会。

Kotlin 在 Android 的位置,不是一两条技术新闻能撼动的。 它背后是 Android 官方支持、Jetpack 生态、Android Studio、社区实践、招聘市场和海量线上项目。

Swift 6.3 现在拿出来的是“首个官方 Android SDK”。 这离大规模生产落地,中间还隔着很多现实问题:

  • 工具链稳定性怎么样
  • 桥接 Kotlin / Java 的成本高不高
  • 调试链路顺不顺
  • CI 能不能稳定接入
  • 团队愿不愿意为它付出学习和维护成本

所以更合理的判断不是“Swift 替代 Kotlin”。

而是:

  • Kotlin 仍然会是 Android 主语言
  • Swift 可能成为共享逻辑和多端模块的新候选
  • 真正决定它能不能站稳的,是工程成熟度,不是新闻热度

04_adoption_stages.png

这一步对 Swift 自己更重要

从更大的角度看,这次最受益的,也许不是 Android,而是 Swift 本身。

一门语言如果长期绑定单平台,它再强,外界对它的认知上限也很明确。

只有当它开始稳定支持更多平台,开发者才会真正把它当成一门独立的软件工程语言,而不是某家平台公司的附属工具。

Swift 6.3 这次最值得玩味的地方就在这里。

它不是一句空泛的“我们支持跨平台”。 而是给出了 SDK、文档、安装方式、构建路径、互操作方案。

这说明 Swift 官方现在回答的问题,已经不再只是:

“Swift 能不能把 Apple 生态服务好?”

而是:

Swift 能不能成为一门覆盖更多平台的软件工程语言?

Android,就是这个问题里最关键的一块拼图。

开发者现在该怎么看

如果你是 Android 开发者,不需要焦虑,也没必要跟风重写任何项目。

你更应该做的是持续观察这几个点:

  • Swift Android SDK 后续更新速度快不快
  • Swift Java 的接入体验到底顺不顺
  • 有没有团队先把 Swift 用到 Android 共享模块里
  • 工具链能不能稳定进入 CI 和生产流程

如果你是 iOS / Swift 开发者,这件事反而更值得持续盯住。

因为这可能是 Swift 在未来两三年里,最有战略意义的一次边界扩张。

它未必马上改变你的项目。 但它很可能会慢慢改变未来的技术选型。

写在最后

Swift 6.3 的重点,不是简单一句“Swift 能写安卓了”。

更准确的说法应该是:

Swift 官方第一次正式发布 Android SDK,并给出了进入现有 Android 工程的可执行路径。

这一步还远远谈不上改写 Android 生态。 但它已经足够说明一件事:

平台边界,还在继续变薄。

你看好 Swift 在 Android 上的发展吗? 你觉得它会先成为共享逻辑工具,还是最终走到更完整的 Android 开发场景?

欢迎评论区聊聊。 如果你身边正好有做 iOS、Android、跨平台架构的朋友,也欢迎把这篇文章转给他们,一起讨论。

参考资料:

  • Swift 官方博客:Swift 6.3 Released
  • Swift 官方文档:Getting Started with the Swift SDK for Android

Swift 6.3 正式发布支持 Android ,它能在跨平台发挥什么优势?

2026年3月30日 11:23

最近 Swift 发布了 6.3 版本,而这个版本最特殊的地方在于:把 Android SDK 作为首个官方发布版本给加了进来,其实这个话题在去年的 《Swift 官方正式支持 Android》我们就已经聊过,而这两天正式版的发布,也是广大 Swift 开发者最高涨的时刻,iOSer 终于也有了自己的原生跨平台基础了:

那 Swfit for Android 到底是什么?其实和之前我们聊的一样,目前并不是在 Android 上原生跑 SwiftUI ,这个能力目前是一个 SKIP 的第三方项目在做, Swfit for Android 主要完成了「Swift 官方支持把 Swift 代码交叉编译到 Android」。

所以 Swfit for Android 目前主要由三个部分组成:

  • Swift Toolchain:目标平台上的 Swift 编译器、标准库、LLVM 后端

  • Swift SDK for Android:给 Android 目标平台准备的 Swift 库、头文件、配置

  • Android NDK:提供 Android 的系统头文件、链接器、目标架构工具链等

也就是说,它的核心原理是「交叉编译」:

  • 需要 macOS / Linux 上装 Swift 工具链
  • 需要 Android target 的 Swift SDK artifact bundle
  • 需要 Android NDK 的 sysroot、linker、headers
  • 由 Swift 编译器把代码交叉编译成 Android 可执行文件或本地库(.so
swift build --swift-sdk x86_64-unknown-linux-android28 --static-swift-stdlib

最终结果来看,就是把代码的构建产物变成 Android 上可运行的 ELF 二进制。

其实这也是在 iOS 的 LLVM 的技术领域,比如 KMP 目前大多也是利用 iOS 分支的 LLVM 交叉编译还到鸿蒙 ,所以 Swift 编译器本身还是走在自己的前端和 LLVM 后端,只是 target 换成 Android,例如:

  • x86_64-unknown-linux-android28
  • aarch64-unknown-linux-android28

这里的 android28 可以看出来,是明确绑定到特定 Android API Level ,而 Swift runtime / Foundation 依赖的一些能力也需要较新的 Android API,所以会用 API 28 为基础。

当然,Swift 与 Android 的 Java/Kotlin 的协调桥梁不出意外是 JNI,当然这里不会让你手写 JNI,而是用自动桥接工具来完成。

而在实际应用层面,Swift 官方现在推荐的不是 “整 App 全 Swift”,而是“Swift 库 + Kotlin/Java 壳” ,比如官方 examples 仓库里推荐方案 `hello-swift-java,它的结构是:

  • 一个 Swift package / Swift library
  • 一个 Kotlin Android app(Jetpack Compose UI)
  • Kotlin 调 Swift,不需要你手写 JNI,交给 swift-java 自动生成 Java wrapper 和 JNI bindings

也就目前而言,推荐的是 business logic / algorithms / libraries 写成 Swift ,而前端仍然保持标准 Kotlin/Java Android app 形态 ,简单来说就是:

  • UI:Kotlin / Jetpack Compose
  • 共享逻辑:Swift
  • 桥接层:swift-java / JNI

这强烈的即视感,不就是最初的 KMP 那会么,CMP 还没支持 UI 的时候,KMP 也是这样的路线

比如 Swift 官方提供的例子:一个 Android ( weather-app )和 Swift 库( weather-lib ),用于获取当前位置的天气信息,weather-lib 内部使用 swift-openapi-generator 调用 OpenMeteo 天气 API ,并公开LocationFetcher protocol , 然后 swift-java 和 JNI 自动生成 Java Wrapper ,从而让 Kotlin 可以直接调用 Swift Library。

而在这次 Swift 官方开源的项目里,核心的项目就是:swift-javaswift-java-jni-core ,他们分别作为“上层桥接工具”与“底层 JNI 基建”支撑起整个 Swfit for Android 的生态。

其实也很有趣,Kotlin 在 Android 跑 JVM ,在 iOS 跑 KN 二进制;而现在反过来 Swift 跑 Android ,也是跑二进制。

swift-java-jni-core

swift-java-jni-core 是一个 Swift-friendly 的 JNI 低层封装,本质上是 jni.h 上的一层薄封装,加上一些预打包的类型转换能力,用来和 JVM / Android Runtime(ART)交互,也就是:

  • 负责 JVM/ART 句柄
  • 找类、找方法
  • 处理线程、锁、引用
  • 做 Java/Swift 类型桥接

它的整个结构大概为:

Sources/  
├── CSwiftJavaJNI/          // C 模块:纯 JNI 头文件 ABI  
└── SwiftJavaJNICore/       // Swift 模块:所有上层封装  
    ├── VirtualMachine/     // JVM 句柄、线程、锁  
    ├── BridgedValues/      // 类型桥接  
    └── *.swift             // 类型系统、签名、Mangling  

整个链路从 Swift 应用代码开始,通过 JavaValue 协议进行类型转换,然后通过 JavaVirtualMachine 管理 JVM 交互,最终利用 CSwiftJavaJNI 的 C 接口调用实际的 JVM 实现:

swift-java

swift-java 是更高一层的互操作工具平台,大致可以分为:

  • Swift 调 Java
  • Java 调 Swift
  • 自动生成绑定代码
  • Android 上支持 jextract --mode=jni,因为 Android/ART 没有服务端 Java 那套 FFM 路线的前提条件

在实现上主要有两个代码生成管道:

1、Swift 调用 Java

  • 通过反射分析 Java 类文件,生成 Swift Wrapper
  • 使用 Swift 宏(@JavaClass@JavaMethod)在编译时展开为 JNI 调用

更具体的就是通过 swift-java wrap-java 命令,工具在运行时利用 Java 反射读取 Java 类(包括 .jar 文件),给每个 Java 类生成对应的 Swift 类型,生成的 Swift 类型使用 @JavaClass@JavaMethod@JavaField 等宏进行标注

用途
@JavaClass 声明一个 Swift 类型是 Java 类的包装
@JavaInterface 声明一个 Swift 类型是 Java 接口的包装
@JavaMethod 将 Swift 方法变为调用 Java 方法的桥接
@JavaField 访问 Java 实例字段
@JavaStaticField 访问 Java 静态字段
@JavaImplementation 在 Swift 中实现 Java native 方法

例如,可以将HelloSwiftMain类型扩展为符合ParsableCommand接口,并使用 Swift 参数解析器来处理 Java 提供的参数:

import ArgumentParser
import SwiftJNI

@JavaClass("org.swift.jni.HelloSwiftMain")
struct HelloSwiftMain: ParsableCommand {
  @Option(name: .shortAndLong, help: "Enable verbose output")
  var verbose: Bool = false

  @JavaImplementation
  static func main(arguments: [String], environment: JNIEnvironment? = nil) {
    let command = Self.parseOrExit(arguments)
    command.run(environment: environment)
  }
  
  func run(environment: JNIEnvironment? = nil) {
    print("Verbose = \(verbose)")
  }
}

所以,对应也存在类型映射的需求:

Java type Swift type
boolean Bool
byte Int8
char UInt16
short Int16
int Int32
long Int64
float Float
double Double
void Void (rare)
T[] [T]
String String
Java class Swift class Swift module
java.lang.Object JavaObject SwiftJava
java.lang.Class<T> JavaClass<T> SwiftJava
java.lang.Throwable Throwable SwiftJava
java.net.URL URL JavaNet

2、Java 调用 Swift(jextract)

主要是让 Java 程序调用 Swift 库 ,生成 Java 绑定和 Swift thunk 文件,支持 FFM 和 JNI 两种生成模式。

比如 jextract 这个流程会分两步:

  • Swift Thunk:用 @_cdecl 暴露 C 符号入口
  • Java 绑定:生成 Java 代码,通过 JNI 或 FFM 调用这些 C 入口

所以,整个 swift-java 主要就是提供自动化方式生成 Swift/Java 绑定,其中:

  • jni 模式兼容性最广,主要支持 Android
  • FFM 模式更偏 Java 22+/25+ 服务端场景,不是 Android 主战场

也就是,swift-java 不只是支持 Android ,它还可以支持 Java Web 场景,野心还是有的。

而整个链路上其实是「Swift - 编译成本地库 - 通过生成的 JNI/Java wrapper 暴露给 Kotlin/Java 调用」 这样一个实现:

image-20260330104416812

那聊到这里,目前局限性也很明显了,它没有一个「 Swift UI for Android 」的支持,它能够让 iOS 的同学把自己的业务逻辑或者纯 Swift 代码共享给 Android ,但是 UI 还是得 Android 自己写。

另外,Swift 社区官方论坛里也有人提到:目前的 Swift Android SDK 下,二进制体积过大,其中一个原因是 Foundation 依赖的 ICU 很重,因为你需要把 Swift runtime、Foundation、ICU 这些东西都带到 Android app 。

同时,在系统 API 上还是差了点意思,比如你需要调用 Andorid 的系统 API 时,大概需要:

Swift ↔ JNI ↔ Java/Kotlin ↔ Android 系统 API

目前这个链路还没有全套完整的官方实现,大概需要后续社区和官方继续补齐,所以当前更多是逻辑和算法等场景的复用,直接调用系统 API 还是会麻烦一些

当然,最终使用过程里,也可以把 Swfit 打包成独立的 Lib,比如在官方例子里的 hello-swift-raw-jni-library ,通过就可以构建出 hello-swift-raw-jni-library-release.aar

./gradlew :hello-swift-raw-jni-library:bundleReleaseAar

所以对于 Swift for Android 来说,目前还是处于起步阶段,作为第一个正式版的起步。

最后,不得不说,语言的最终归宿就是跨平台,UI 的最终归宿也是,现在的 Swift for Android 就像是当年的 KMP ,从语言的跨平台开始切入,未来要发展, Swift UI for Android 的路径看起来也不是不可能,至于最终能否发展起来,这就考验 Swift 社区的运营水平了。

image.png

链接

github.com/swiftlang/s…

github.com/swiftlang/s…

www.swift.org/documentati…

【Flutter×鸿蒙】通关手册(二):FVM 不认鸿蒙 SDK?4步手动塞进去

作者 TT_Close
2026年3月5日 18:26

系列导航:

我第一次让 FVM 管理鸿蒙版 Flutter SDK 时,前后踩了 4 个坑,花了大半天才跑通。事后复盘发现,每个坑都不难,只是没人提前告诉我"为什么要这样做"。这篇把整个过程拆成 5 关,每关讲清「为什么」和「怎么做」,争取让你 20 分钟一次通关。

前置条件:请先完成第一篇的全部内容——DevEco Studio 已安装,ohpm、node、hvigorw 在终端里都能正常调用。


🗺️ 通关路线图

关卡 任务 预计耗时
第1关 安装 FVM 2 min
第2关 克隆鸿蒙版 SDK 5 min(取决于网速)
第3关 修复版本"身份证" 3 min
第4关 指定鸿蒙 SDK 路径 1 min
第5关 全绿验证 2 min

🎯 第 1 关:安装 FVM

目标

让终端认识 fvm 命令。

为什么需要 FVM

一句话——让不同项目用不同版本的 Flutter,互不干扰。比如项目 A 用官方 3.24 跑 Android/iOS,项目 B 用鸿蒙版 3.35.8。FVM 就是 Flutter 的"版本档案柜",每个抽屉放一个版本。

📋 操作

# macOS(在终端里执行,这是用 Homebrew 包管理器安装 FVM)
brew install fvm
# Windows(在 cmd 或 PowerShell 中执行,这是用 Chocolatey 包管理器安装 FVM)
choco install fvm

安装完后,配置 FVM 缓存路径。把以下两行写入 ~/.zshrc(上一篇介绍过,这是 Mac 终端的配置文件):

# FVM 存放所有 Flutter 版本的目录
export FVM_CACHE_PATH=$HOME/fvm
# 让 FVM 的默认版本可以直接用 flutter 命令调用
export PATH="$HOME/fvm/default/bin:$PATH"

保存后执行下面这条命令,让刚才的配置立即生效(否则要关掉终端重新打开):

source ~/.zshrc

✅ 验证

# 查看 FVM 版本号,确认安装成功
fvm --version

看到版本号(如 3.1.4)就过关了。

⚠️ 如果报 command not found:Mac 用户确认已安装 Homebrew(执行 brew --version 看有没有输出);Windows 用户确认已安装 Chocolatey(执行 choco --version)。如果包管理器本身都没装,请先去官网安装。


🎯 第 2 关:克隆鸿蒙版 SDK

目标

把华为的鸿蒙版 Flutter 放进 FVM 管辖。

为什么不能直接 fvm install

正常装 Flutter 只需要 fvm install 3.24.0,FVM 会自动去 GitHub 下载。但鸿蒙版是华为团队在 AtomGit(国内代码托管平台)上单独维护的,FVM 的世界里它根本不存在。所以我们要"手动入库"——自己下载代码,放到 FVM 的档案柜里,假装它一直在那。

⚠️ 本关最大的坑:分支名和版本号是两回事!

仓库的分支叫 oh-3.35.7-dev,看到 3.35.7 你会以为版本就是 3.35.7。但实际上代码里的版本已经迭代到了 3.35.8-ohos-0.0.2

类比:Git 分支叫 feature/login-v1,但代码早就改到 v3 了。分支名是创建时起的,不会跟着版本号自动更新。

千万别拿分支名当版本号用,团队必须统一用 3.35.8-ohos-0.0.2 这个真实版本号。

📋 操作

# --depth 1 只取最新代码,省空间(省去几个 GB 的历史记录)
git clone -b oh-3.35.7-dev --depth 1 
https://atomgit.com/openharmony-tpc/flutter_flutter.git 
~/fvm/versions/3.35.8-ohos-0.0.2

注意看:clone 命令里分支名是 oh-3.35.7-dev,但目标文件夹名是 3.35.8-ohos-0.0.2——这不是写错了,上面已经解释了为什么不一样。

💡 怎么确认真实版本号? clone 完后执行 ~/fvm/versions/3.35.8-ohos-0.0.2/bin/flutter --version 看输出。如果加入已有团队,直接看项目的 .fvmrc 文件(命令:cat .fvmrc)。

✅ 验证

# 确认文件下载成功(ls = 列出目录内容)
ls ~/fvm/versions/3.35.8-ohos-0.0.2/bin/flutter

文件存在就过关。

⚠️ 如果报 No such file or directory:回去检查 clone 命令是否执行成功。常见原因是网络超时(AtomGit 在国内,通常不需要梯子,但公司内网可能有限制)。重新执行 clone 前,先删掉残留目录:rm -rf ~/fvm/versions/3.35.8-ohos-0.0.2,再重试。


🎯 第 3 关:修复版本"身份证"

目标

让 FVM 正确识别这个 SDK 的版本号。

为什么要做这步

clone 下来的 SDK 有两张"证件":

  1. version 文件——相当于身份证,一行文本写着版本号
  2. bin/cache/flutter.version.json——相当于内部档案,JSON 格式的详细版本信息

问题是,这两张证件上都写着 0.0.0-unknown(因为鸿蒙团队是从开发分支构建的,没有打标准标签)。但我们的文件夹名叫 3.35.8-ohos-0.0.2。FVM 一查——名字对不上,直接翻脸。

⚠️ 不做这步的后果:FVM 会弹出 "Version mismatch" 并试图删掉你的 SDK 重装。如果看到了这个弹窗,千万不要选任何选项,按 Ctrl+C(Mac 也是 Ctrl 不是 Cmd)退出,回来做这步。

📋 操作

macOS / Linux:

cd ~/fvm/versions/3.35.8-ohos-0.0.2

# 第一步:改"身份证"
echo -n "3.35.8-ohos-0.0.2" > version

# 第二步:初始化 Flutter 引擎(首次运行会下载 Dart SDK,需要等 1-3 分钟)
bin/flutter --version

# 第三步:改"内部档案"(把所有 0.0.0-unknown 替换成正确的版本号)
sed -i '' 's/0.0.0-unknown/3.35.8-ohos-0.0.2/g' bin/cache/flutter.version.json

Windows PowerShell:

# 进入 SDK 所在目录
cd $env:USERPROFILE\fvm\versions\3.35.8-ohos-0.0.2

# 第一步:改"身份证"
"3.35.8-ohos-0.0.2" | Set-Content version -NoNewline

# 第二步:初始化引擎
bin\flutter --version

# 第三步:改"内部档案"(PowerShell 的查找替换写法)
(Get-Content bin\cache\flutter.version.json) -replace '0.0.0-unknown', '3.35.8-ohos-0.0.2' | Set-Content bin\cache\flutter.version.json

⚠️ 三步的顺序不能乱——第二步会生成 flutter.version.json 文件,第三步才有东西可改。如果你先执行了第三步,会报文件不存在。

✅ 验证

# 回到任意目录都可以执行(fvm list = 列出 FVM 管理的所有 Flutter 版本)
fvm list

看到 Version 列显示 3.35.8-ohos-0.0.2(不是空白、不是 Need setup、不是 0.0.0-unknown),这关就过了。

02_fvm_list.png ⚠️ 如果还是显示异常,逐一排查两张"证件":

# 检查"身份证"内容
cat ~/fvm/versions/3.35.8-ohos-0.0.2/version
# 应该输出:3.35.8-ohos-0.0.2(没有多余空行)

# 检查"内部档案"有没有残留的 0.0.0-unknown
cat ~/fvm/versions/3.35.8-ohos-0.0.2/bin/cache/flutter.version.json
# 里面所有 version 字段应该都是 3.35.8-ohos-0.0.2

如果 version 文件内容不对,重新执行第一步;如果 JSON 里还有 0.0.0-unknown,重新执行第三步。


🎯 第 4 关:指定鸿蒙 SDK 路径

目标

让 Flutter 知道鸿蒙的 SDK(OpenHarmony SDK)装在哪。

为什么不用环境变量

我试过 HOS_SDK_HOMEOHOS_SDK_HOME 等环境变量,时灵时不灵。原因是不同方式打开的终端(VS Code 内置终端 vs 系统终端 vs CI 环境)加载配置文件的顺序不一样,变量可能没被读到。flutter config 会把路径写入 Flutter 自己的配置文件,不管从哪里启动都能读到,最稳。

📋 操作

# 把鸿蒙 SDK 的位置"写死"到 Flutter 的配置里(一次性操作)
~/fvm/versions/3.35.8-ohos-0.0.2/bin/flutter config \
--ohos-sdk="/Applications/DevEco-Studio.app/Contents/sdk"

⚠️ Windows 用户路径改为:--ohos-sdk="C:\Program Files\Huawei\DevEco Studio\sdk"

请根据 DevEco Studio 实际安装路径调整。不确定装在哪?打开 DevEco Studio → Settings → SDK 页面可以看到路径。

终端输出 Setting "ohos-sdk" value to "..." 就成功了。

✅ 验证

不急,下一关一起验收。


🎯 第 5 关:全绿验证

目标

flutter doctor 中 HarmonyOS toolchain 一栏显示绿色对勾。

📋 操作

# 运行 Flutter 的环境诊断工具(-v 表示显示详细信息)
~/fvm/versions/3.35.8-ohos-0.0.2/bin/flutter doctor -v

✅ 验证

关注输出中的 HarmonyOS 那一栏:

[✓] HarmonyOS toolchain - develop for HarmonyOS devices
    • OpenHarmony Sdk at /Applications/DevEco-Studio.app/Contents/sdk,
      available api versions has [22:default]
    • Ohpm version 6.0.1
    • Node version v18.20.1
    • Hvigorw binary at .../hvigor/bin/hvigorw

看到 [✓] 加上 4 个子项都有值 = 通关!

02_flutter_doctor.png 💡 你可能会看到 Flutter 那栏有几个 ! 警告(channel 不标准、upstream 不是官方地址)。这是鸿蒙版的正常现象,完全不影响开发和打包,放心忽略。

⚠️ 如果 HarmonyOS 那栏还是红叉,按优先级排查:

  1. SDK not found → 回第 4 关检查 config 路径是否正确
  2. ohpm/hvigorw missing → 回第一篇检查环境变量
  3. Version mismatch → 回第 3 关检查两张"证件"

🔧 附加关:FVM 的"碎碎念"

通关后你会发现,每次用 fvm flutter xxx 时 FVM 都会弹 "not a valid version" 的警告让你确认。这不是报错,只是 FVM 在说:"这个版本号我在官方列表里查不到,你确定要用吗?"

三种应对方式:

  1. 手动按 y——每次弹出输入 y 回车
  2. 自动确认——命令前加 yes |
yes | fvm flutter doctor
  1. 绕过 FVM——直接用绝对路径调用,完全不弹警告:
~/fvm/versions/3.35.8-ohos-0.0.2/bin/flutter doctor

我推荐第三种,路径虽长但最省心。可以设个快捷方式(alias)缩短它:

# 把这行加到 ~/.zshrc 里(alias = 给一条长命令起个短名字)
alias hflutter="$HOME/fvm/versions/3.35.8-ohos-0.0.2/bin/flutter"

保存后 source ~/.zshrc,之后直接 hflutter runhflutter doctor 就行。


🏆 通关总结

项目 状态
FVM ✅ 已安装
鸿蒙版 Flutter SDK ✅ ~/fvm/versions/3.35.8-ohos-0.0.2
version 文件 ✅ 已修复
flutter.version.json ✅ 已修复
flutter config --ohos-sdk ✅ 已配置
flutter doctor HarmonyOS ✅ 全绿

回顾核心逻辑:FVM 只管官方 Flutter,鸿蒙版要我们手动塞进去(第 2 关);塞进去后"证件"信息对不上,需要手动修正(第 3 关);最后告诉 Flutter 鸿蒙 SDK 在哪(第 4 关)。理解了这条线,以后鸿蒙版 SDK 升级换版本号,你也能照样搞定。

如果中途卡住,大概率是版本号写错了——检查文件夹名、version 文件内容、flutter.version.json 里的版本号,三者必须完全一致


下一篇预告:SDK 准备好了,接下来要把你的老 Flutter 项目跑到鸿蒙上——听起来就是敲几行命令的事?没那么简单。→ 【Flutter×鸿蒙】通关手册(三):debug 包也要签名,这点和 Android 差远了

iOS + AI ,国外一个叫 Rork Max 的项目打算替换掉 Xcode

2026年2月21日 17:34

最近看到一个很有意思的项目,它是一个由国外 Rork 团队推出的 AI 移动应用开发平台,宣称是“全球首个在浏览器中构建原生 Swift 应用的 AI 工具”,也就是,你可以不需要 Mac 和 Xcode ,同时一次性完成 iPhone、手表、iPad、电视和 Vision Pro 的应用,甚至还有 AR 和 3D 支持。

所以它的产品逻辑是:用户只需在浏览器中输入自然语言描述,AI 就会自动生成 SwiftUI 代码,然后编译并在云端模拟器中运行,最后支持一键发布到 App Store。

什么 swift 版本 uniapp ?

听起来有点玄乎,但是实际上其实就是 Rork 在后端部署了大量的物理 Mac 节点或 Mac 云实例,当你开始一个项目时,系统就会动态分配一台运行着 XcodeiOS SDK 的 Mac 给对应会话。

也就是所有的编译、链接、资产打包过程都在真实的 macOS 环境下完成,生成的是 100% 的原生 Swift/SwiftUI 代码。

所以实际上就是:Cloud 版本的 Xcode/Mac ,然后搭配 Claude Code 和 Opus 4.6 ,然后生成对应的 iOS App 并提交 Apple Store 审核。

而 Rork 在这里也是采用了类似于云游戏的实时视频流协议(低延迟传输),所以你在浏览器里的每一次点击都会传回云端 Mac 的模拟器,画面变化再实时推送到前端

实际上就是一个远程主机,本质和 AI Studio 类似。

当然,Rork Max 的核心肯定还是他们的 Agent 管理和产品流程,这里的 AI Agent 除了利用 Opus 4.6 写代码之外,还要管理它的所有报错,测试运行和工程管理,同时 Rork 内置了 App Store Connect 的自动化流程,用户登录 Apple ID 后,AI 可以代理证书配置、App 打包和提审等流程。

从这里看,Rork Max 的客户更多的可能是非开发者,所以它的目标是将复杂的工程基座(Mac 硬件 + Xcode SDK + 苹果证书体系)完全抽象化,让开发者只需要关注逻辑和创意

另外,这里 Rork 自己强调了“非模版化”。它不是通过预设模版拼凑应用,而是通过大模型实时推理,通过自己实现的“持续上下文注入”的技术,让 AI 记住你之前所有对 UI 的微调,确保跨平台迁移时风格的一致性。

实际上它更多是一个从零构建、测试、安装并上架的 Apple 体系生产平台。它直接把“idea → 上架 App Store 的原生 Swift 应用”压缩成一个网页操作,从而大幅度降低了门槛。

官方演示视频中,从零到可玩的游戏原型大概 30–60 分钟:

另外 Rork 也表示后续会支持直接导入老项目的功能,不过对于这种场景,基本都是已经有开发者维护的项目场景,我比较怀疑是否会有受众,虽然貌似真的有:

目前已经有一些 Rork max 用户开始体验,反馈褒贬不一,但是我是没真实体验的,因为 Rork Max 的价格还是挺感人:

为什么不体验其他的?因为我看到所有说不好用的回复里,官方都是问:你是否打开了 Rork Max

当然,觉得它有意思的原因,也是它这个产品形态或者是未来的代表之一,开发者不再需要装什么 IDE 或者 SDK ,甚至都不需要纠结是 win 还是 mac 甚至 linux ,只需要一个入口,就可以完成需要开发,当然,那时候如果真的到来的话,也许开发者也不是开发者了,可能更多只是 token 账单的消费者。

❌
❌