同一份 Kotlin 业务代码,KMP 编译器针对不同目标平台输出不同形态的二进制。宿主 App 接入 Kuikly 跟接入一个普通的 SDK 没区别。
| 平台 | Kuikly | Flutter | RN | Compose MP |
|---|---|---|---|---|
| Android | ✅ | ✅ | ✅ | ✅ |
| iOS | ✅ | ✅ | ✅ | ✅ |
| HarmonyOS Next | ✅ 一等公民 | ⚠️ 第三方 | ⚠️ 第三方 | ❌ |
| Web | ✅ | ✅ | ✅ | ✅ |
| 微信小程序 | ✅ | ❌ | ❌ | ❌ |
| macOS / Win / Linux | ⚠️ macOS Alpha | ✅ | ⚠️ | ✅ |
同一个用户操作(比如点击按钮),从你的代码到屏幕上像素的链路对比。步骤越少、桥接越少 = 性能越好。
✅ 无桥接、无序列化、产物即原生模块
⚠ 高频调用时桥接序列化打爆主线程
✅ 性能强,但包大、跟系统原生有差异
✅ 跟 Compose 同语法,体验类似 Flutter
体积是跨端框架的关键指标。包越大,用户下载等待时间越长,应用商店转化率越低。
| 用户场景 | Kuikly +1MB | Flutter +6MB |
|---|---|---|
| 4G 下首次下载耗时 | +1 秒 | +6 秒 |
| Wi-Fi 下首次下载 | +0.1 秒 | +0.6 秒 |
| 渠道包体积红线(如 Google Play 50MB 限制) | 压力小 | 压力大 |
| 下载转化率影响 | 几乎无 | 每多 6MB ≈ 转化率 -1% |
下面有两个按钮,模拟 Kuikly 的"直调"和 RN 的"桥接调用"。点击它们,观察响应速度和操作流畅度。
需求:屏幕中央显示 "Hello {计数}",右下角放一个按钮,点击让计数 +1。
@Page("Counter")
class CounterPage : Pager() {
private var count by observable(0)
override fun body(): ViewBuilder {
val ctx = this
return {
attr {
allCenter()
backgroundColor(Color.WHITE)
}
Text {
attr {
text("Hello ${ctx.count}")
fontSize(20f)
fontWeightBold()
}
}
Button {
attr {
absolutePosition(bottom=30f, right=30f)
size(80f, 80f)
borderRadius(10f)
backgroundColor(Color.BLUE)
}
event { click { ctx.count++ } }
}
}
}
}
class Counter extends StatefulWidget {
@override _S createState() => _S();
}
class _S extends State<Counter> {
int count = 0;
@override
Widget build(c) => Scaffold(
body: Stack(children: [
Center(
child: Text('Hello $count',
style: TextStyle(fontSize: 20))),
Positioned(right: 30, bottom: 30,
child: GestureDetector(
onTap: () =>
setState(() => count++),
child: Container(
width: 80, height: 80,
decoration: BoxDecoration(
color: Colors.blue,
borderRadius:
BorderRadius.circular(10))))),
]),
);
}
import { useState } from 'react';
import { View, Text,
TouchableOpacity }
from 'react-native';
export default function() {
const [c, setC] = useState(0);
return (
<View style={{flex:1,
justifyContent:'center',
alignItems:'center'}}>
<Text style={{fontSize:20}}>
Hello {c}
</Text>
<TouchableOpacity
onPress={() => setC(c+1)}
style={{
position:'absolute',
bottom:30, right:30,
width:80, height:80,
borderRadius:10,
backgroundColor:'blue'}}/>
</View>
);
}
所谓"几棵树"指的是从 DSL 到原生控件之间,要经过多少层中间表示。层数越多,开销越大。
Widget Tree → Element Tree → RenderObject Tree
(描述) (生命周期) (布局 + 绘制)
不可变 可变 可变
每次重建 Diff 复用 实际工作
BuildTree → RenderTree
(原型树:组件 + 布局节点) (渲染树:仅可见节点)
┌─ Pager ┌─ Pager
├─ View (布局容器) ──► ├─ Text (可见)
│ ├─ Text (可见) └─ Button (可见)
│ └─ Button (可见)
└─ ScrollView (布局容器)
※ 布局容器节点不渲染 ※ Diff 时只对比这一棵
※ 测量、布局都在这棵完成 ※ 跟 Native View 1:1 映射
跨端层和 Native 层之间通过指令通信(不是直接函数调用),这是 Kuikly 区别于 Compose MP 的关键:
// 跨端 Kotlin 层发出渲染指令
createNode(id=1, type="Text")
setProp(1, "text", "Hello 5")
setProp(1, "fontSize", 20)
addChild(parent=0, child=1)
// Android Native 层接收并执行
TextView tv = new TextView(ctx);
tv.setText("Hello 5");
tv.setTextSize(20);
parentView.addView(tv);
这种"指令通信"的好处是:跨端层可以独立打包、独立动态化下发,不直接依赖原生层的具体实现。这是 Kuikly 动态化的基础。