# 平台与功能支持矩阵

> 多模块模型（core + launchers）以及每个平台所支持的功能。

Source: https://crossfly.dev/zh/docs/platforms/overview — CrossFly documentation. Full docs as one file: https://crossfly.dev/llms-full.txt

# 平台与功能支持矩阵

CrossFly 是一个 **Gradle 多模块**项目：UI 和逻辑都位于 **`core`**（中立层），而每个
**launcher**（`lwjgl3`、`android`、`ios`、`web`）只负责操作系统的*引导*（bootstrap）。同一份 Java 代码可以
在这四个目标平台上运行。

<div class="cf-diagram">
<svg role="img" id="my-svg" width="1005" height="164" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="flowchart" style="max-width: 1004.98px; background-color: transparent;" viewBox="0 0 1004.984375 164" role="graphics-document document" aria-roledescription="flowchart-v2"><style>#my-svg{font-family:Manrope,system-ui,sans-serif;font-size:14px;fill:var(--text);}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#my-svg .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#my-svg .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#my-svg .error-icon{fill:var(--bg-elev);}#my-svg .error-text{fill:var(--text-dim);stroke:var(--text-dim);}#my-svg .edge-thickness-normal{stroke-width:1px;}#my-svg .edge-thickness-thick{stroke-width:3.5px;}#my-svg .edge-pattern-solid{stroke-dasharray:0;}#my-svg .edge-thickness-invisible{stroke-width:0;fill:none;}#my-svg .edge-pattern-dashed{stroke-dasharray:3;}#my-svg .edge-pattern-dotted{stroke-dasharray:2;}#my-svg .marker{fill:var(--text-dim);stroke:var(--text-dim);}#my-svg .marker.cross{stroke:var(--text-dim);}#my-svg svg{font-family:Manrope,system-ui,sans-serif;font-size:14px;}#my-svg p{margin:0;}#my-svg .label{font-family:Manrope,system-ui,sans-serif;color:var(--text);}#my-svg .cluster-label text{fill:var(--text-dim);}#my-svg .cluster-label span{color:var(--text-dim);}#my-svg .cluster-label span p{background-color:transparent;}#my-svg .label text,#my-svg span{fill:var(--text);color:var(--text);}#my-svg .node rect,#my-svg .node circle,#my-svg .node ellipse,#my-svg .node polygon,#my-svg .node path{fill:var(--bg-soft);stroke:var(--border-strong);stroke-width:1px;}#my-svg .rough-node .label text,#my-svg .node .label text,#my-svg .image-shape .label,#my-svg .icon-shape .label{text-anchor:middle;}#my-svg .node .katex path{fill:var(--text-dim);stroke:var(--text-dim);stroke-width:1px;}#my-svg .rough-node .label,#my-svg .node .label,#my-svg .image-shape .label,#my-svg .icon-shape .label{text-align:center;}#my-svg .node.clickable{cursor:pointer;}#my-svg .root .anchor path{fill:var(--text-dim)!important;stroke-width:0;stroke:var(--text-dim);}#my-svg .arrowheadPath{fill:rgba(255, 255, 255, 0);}#my-svg .edgePath .path{stroke:var(--text-dim);stroke-width:1px;}#my-svg .flowchart-link{stroke:var(--text-dim);fill:none;}#my-svg .edgeLabel{background-color:var(--bg-elev);text-align:center;}#my-svg .edgeLabel p{background-color:var(--bg-elev);}#my-svg .edgeLabel rect{opacity:0.5;background-color:var(--bg-elev);fill:var(--bg-elev);}#my-svg .labelBkg{background-color:rgba(255, 255, 255, 0.5);}#my-svg .cluster rect{fill:var(--bg-elev);stroke:hsl(0, 0%, 90%);stroke-width:1px;}#my-svg .cluster text{fill:var(--text-dim);}#my-svg .cluster span{color:var(--text-dim);}#my-svg div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:Manrope,system-ui,sans-serif;font-size:12px;background:var(--bg-elev);border:1px solid hsl(0, 0%, 90%);border-radius:2px;pointer-events:none;z-index:100;}#my-svg .flowchartTitleText{text-anchor:middle;font-size:18px;fill:var(--text);}#my-svg rect.text{fill:none;stroke-width:0;}#my-svg .icon-shape,#my-svg .image-shape{background-color:var(--bg-elev);text-align:center;}#my-svg .icon-shape p,#my-svg .image-shape p{background-color:var(--bg-elev);padding:2px;}#my-svg .icon-shape .label rect,#my-svg .image-shape .label rect{opacity:0.5;background-color:var(--bg-elev);fill:var(--bg-elev);}#my-svg .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#my-svg .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#my-svg .node .neo-node{stroke:var(--border-strong);}#my-svg [data-look="neo"].node rect,#my-svg [data-look="neo"].cluster rect,#my-svg [data-look="neo"].node polygon{stroke:url(#my-svg-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#my-svg [data-look="neo"].swimlane.cluster rect{filter:none;}#my-svg [data-look="neo"].node path{stroke:url(#my-svg-gradient);stroke-width:1px;}#my-svg [data-look="neo"].node .outer-path{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#my-svg [data-look="neo"].node .neo-line path{stroke:var(--border-strong);filter:none;}#my-svg [data-look="neo"].node circle{stroke:url(#my-svg-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#my-svg [data-look="neo"].node circle .state-start{fill:var(--text-dim);}#my-svg [data-look="neo"].icon-shape .icon{fill:url(#my-svg-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#my-svg [data-look="neo"].icon-shape .icon-neo path{stroke:url(#my-svg-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#my-svg :root{--mermaid-font-family:Manrope,system-ui,sans-serif;}</style><g><marker id="my-svg_flowchart-v2-pointEnd" class="marker flowchart-v2" viewBox="0 0 10 10" refX="5" refY="5" markerUnits="userSpaceOnUse" markerWidth="8" markerHeight="8" orient="auto"><path d="M 0 0 L 10 5 L 0 10 z" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"/></marker><marker id="my-svg_flowchart-v2-pointStart" class="marker flowchart-v2" viewBox="0 0 10 10" refX="4.5" refY="5" markerUnits="userSpaceOnUse" markerWidth="8" markerHeight="8" orient="auto"><path d="M 0 5 L 10 10 L 10 0 z" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"/></marker><marker id="my-svg_flowchart-v2-pointEnd-margin" class="marker flowchart-v2" viewBox="0 0 11.5 14" refX="11.5" refY="7" markerUnits="userSpaceOnUse" markerWidth="10.5" markerHeight="14" orient="auto"><path d="M 0 0 L 11.5 7 L 0 14 z" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"/></marker><marker id="my-svg_flowchart-v2-pointStart-margin" class="marker flowchart-v2" viewBox="0 0 11.5 14" refX="1" refY="7" markerUnits="userSpaceOnUse" markerWidth="11.5" markerHeight="14" orient="auto"><polygon points="0,7 11.5,14 11.5,0" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"/></marker><marker id="my-svg_flowchart-v2-circleEnd" class="marker flowchart-v2" viewBox="0 0 10 10" refX="11" refY="5" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"/></marker><marker id="my-svg_flowchart-v2-circleStart" class="marker flowchart-v2" viewBox="0 0 10 10" refX="-1" refY="5" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"/></marker><marker id="my-svg_flowchart-v2-circleEnd-margin" class="marker flowchart-v2" viewBox="0 0 10 10" refY="5" refX="12.25" markerUnits="userSpaceOnUse" markerWidth="14" markerHeight="14" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"/></marker><marker id="my-svg_flowchart-v2-circleStart-margin" class="marker flowchart-v2" viewBox="0 0 10 10" refX="-2" refY="5" markerUnits="userSpaceOnUse" markerWidth="14" markerHeight="14" orient="auto"><circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"/></marker><marker id="my-svg_flowchart-v2-crossEnd" class="marker cross flowchart-v2" viewBox="0 0 11 11" refX="12" refY="5.2" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><path d="M 1,1 l 9,9 M 10,1 l -9,9" class="arrowMarkerPath" style="stroke-width: 2; stroke-dasharray: 1, 0;"/></marker><marker id="my-svg_flowchart-v2-crossStart" class="marker cross flowchart-v2" viewBox="0 0 11 11" refX="-1" refY="5.2" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"><path d="M 1,1 l 9,9 M 10,1 l -9,9" class="arrowMarkerPath" style="stroke-width: 2; stroke-dasharray: 1, 0;"/></marker><marker id="my-svg_flowchart-v2-crossEnd-margin" class="marker cross flowchart-v2" viewBox="0 0 15 15" refX="17.7" refY="7.5" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto"><path d="M 1,1 L 14,14 M 1,14 L 14,1" class="arrowMarkerPath" style="stroke-width: 2.5;"/></marker><marker id="my-svg_flowchart-v2-crossStart-margin" class="marker cross flowchart-v2" viewBox="0 0 15 15" refX="-3.5" refY="7.5" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto"><path d="M 1,1 L 14,14 M 1,14 L 14,1" class="arrowMarkerPath" style="stroke-width: 2.5; stroke-dasharray: 1, 0;"/></marker><g class="root"><g class="clusters"/><g class="edgePaths"><path d="M363.594,47.162L320.096,52.968C276.599,58.775,189.604,70.387,146.107,79.694C102.609,89,102.609,96,102.609,99.5L102.609,103" id="my-svg-L_Core_L1_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_Core_L1_0" data-points="W3sieCI6MzYzLjU5Mzc1LCJ5Ijo0Ny4xNjE3NjEyOTc3OTgzOH0seyJ4IjoxMDIuNjA5Mzc1LCJ5Ijo4Mn0seyJ4IjoxMDIuNjA5Mzc1LCJ5IjoxMDd9XQ==" data-look="classic" marker-end="url(#my-svg_flowchart-v2-pointEnd)"/><path d="M410.32,57L399.587,61.167C388.854,65.333,367.388,73.667,356.655,81.333C345.922,89,345.922,96,345.922,99.5L345.922,103" id="my-svg-L_Core_L2_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_Core_L2_0" data-points="W3sieCI6NDEwLjMxOTc2MDEwMTAxMDEsInkiOjU3fSx7IngiOjM0NS45MjE4NzUsInkiOjgyfSx7IngiOjM0NS45MjE4NzUsInkiOjEwN31d" data-look="classic" marker-end="url(#my-svg_flowchart-v2-pointEnd)"/><path d="M536.54,57L547.273,61.167C558.006,65.333,579.472,73.667,590.205,81.333C600.938,89,600.938,96,600.938,99.5L600.938,103" id="my-svg-L_Core_L3_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_Core_L3_0" data-points="W3sieCI6NTM2LjUzOTYxNDg5ODk4OTgsInkiOjU3fSx7IngiOjYwMC45Mzc1LCJ5Ijo4Mn0seyJ4Ijo2MDAuOTM3NSwieSI6MTA3fV0=" data-look="classic" marker-end="url(#my-svg_flowchart-v2-pointEnd)"/><path d="M583.266,45.968L632.241,51.973C681.216,57.979,779.167,69.989,828.142,79.495C877.117,89,877.117,96,877.117,99.5L877.117,103" id="my-svg-L_Core_L4_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_Core_L4_0" data-points="W3sieCI6NTgzLjI2NTYyNSwieSI6NDUuOTY4MDM4NzgzMDkzMzU1fSx7IngiOjg3Ny4xMTcxODc1LCJ5Ijo4Mn0seyJ4Ijo4NzcuMTE3MTg3NSwieSI6MTA3fV0=" data-look="classic" marker-end="url(#my-svg_flowchart-v2-pointEnd)"/></g><g class="edgeLabels"><g class="edgeLabel"><g class="label" data-id="L_Core_L1_0" transform="translate(0, 0)"><foreignObject width="0" height="0"><div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="edgeLabel"><g class="label" data-id="L_Core_L2_0" transform="translate(0, 0)"><foreignObject width="0" height="0"><div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="edgeLabel"><g class="label" data-id="L_Core_L3_0" transform="translate(0, 0)"><foreignObject width="0" height="0"><div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="edgeLabel"><g class="label" data-id="L_Core_L4_0" transform="translate(0, 0)"><foreignObject width="0" height="0"><div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="edgeLabel"></span></div></foreignObject></g></g></g><g class="nodes"><g class="node default" id="my-svg-flowchart-Core-0" data-look="classic" transform="translate(473.4296875, 32.5)"><rect class="basic label-container" style="" x="-109.8359375" y="-24.5" width="219.671875" height="49"/><g class="label" style="" transform="translate(-81.8359375, -10.5)"><rect/><foreignObject width="163.671875" height="21"><div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="nodeLabel"><p><b>core</b> — UI + 逻辑（中立）</p></span></div></foreignObject></g></g><g class="node default" id="my-svg-flowchart-L1-2" data-look="classic" transform="translate(102.609375, 131.5)"><rect class="basic label-container" style="" x="-94.609375" y="-24.5" width="189.21875" height="49"/><g class="label" style="" transform="translate(-66.609375, -10.5)"><rect/><foreignObject width="133.21875" height="21"><div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="nodeLabel"><p>🖥️ lwjgl3 → LWJGL3</p></span></div></foreignObject></g></g><g class="node default" id="my-svg-flowchart-L2-4" data-look="classic" transform="translate(345.921875, 131.5)"><rect class="basic label-container" style="" x="-98.703125" y="-24.5" width="197.40625" height="49"/><g class="label" style="" transform="translate(-70.703125, -10.5)"><rect/><foreignObject width="141.40625" height="21"><div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="nodeLabel"><p>🤖 android → Android</p></span></div></foreignObject></g></g><g class="node default" id="my-svg-flowchart-L3-6" data-look="classic" transform="translate(600.9375, 131.5)"><rect class="basic label-container" style="" x="-106.3125" y="-24.5" width="212.625" height="49"/><g class="label" style="" transform="translate(-78.3125, -10.5)"><rect/><foreignObject width="156.625" height="21"><div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="nodeLabel"><p>🍎 ios → RoboVM (AOT)</p></span></div></foreignObject></g></g><g class="node default" id="my-svg-flowchart-L4-8" data-look="classic" transform="translate(877.1171875, 131.5)"><rect class="basic label-container" style="" x="-119.8671875" y="-24.5" width="239.734375" height="49"/><g class="label" style="" transform="translate(-91.8671875, -10.5)"><rect/><foreignObject width="183.734375" height="21"><div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"><span class="nodeLabel"><p>🌐 web → TeaVM + WebGL2</p></span></div></foreignObject></g></g></g></g></g><defs><filter id="my-svg-drop-shadow" height="130%" width="130%"><feDropShadow dx="4" dy="4" stdDeviation="0" flood-opacity="0.06" flood-color="var(--text-dim)"/></filter></defs><defs><filter id="my-svg-drop-shadow-small" height="150%" width="150%"><feDropShadow dx="2" dy="2" stdDeviation="0" flood-opacity="0.06" flood-color="var(--text-dim)"/></filter></defs><linearGradient id="my-svg-gradient" gradientUnits="objectBoundingBox" x1="0%" y1="0%" x2="100%" y2="0%"><stop offset="0%" stop-color="var(--border-strong)" stop-opacity="1"/><stop offset="100%" stop-color="hsl(0, 0%, 90%)" stop-opacity="1"/></linearGradient></svg>
</div>

## 平台检测
```java
if (Device.platform.isDesktop()) { ... }   // WINDOWS/LINUX/MACOS
if (Device.platform.isMobile()) { ... }     // ANDROID/IPHONE/IPAD
if (Device.platform.isIOS()) { ... }
```
`Platform`：`JAVA` · `WINDOWS` · `LINUX` · `IPHONE` · `ANDROID` · `IPAD` · `MACOS`。

## 按功能划分的支持矩阵
| 功能 | 🖥️ 桌面端 | 🤖 Android | 🍎 iOS | 🌐 Web |
|---|:--:|:--:|:--:|:--:|
| 渲染 UI | ✅ | ✅ | ✅ | ✅ |
| 相机 / 条码 | ⚠️¹ | ✅ | ✅ | ❌ |
| GPS / 定位 | ❌ | ✅ | ✅ | ❌ |
| Bluetooth BLE | ⚠️ | ✅ | ✅ | ❌ |
| 生物识别 | ⚠️ | ✅ | ✅ | ❌ |
| 原生地图 | ✅ | ✅ | ✅ | ❌ |
| `WebContainer` | ✅ | ✅ | ✅² | ❌³ |
| 振动 | ❌ | ✅ | ✅ | ❌ |
| 语音 (TTS/STT) | ⚠️ | ✅ | ✅ | ❌ |
| 文件持久化 | ✅ | ✅ | ✅ | ❌⁴ |
| HTTP | ✅ | ✅ | ✅ | ⚠️⁵ |

✅ 支持 · ⚠️ 有限制/因情况而异 · ❌ *stub*（不执行任何有用操作）
1. 桌面端的相机因操作系统而异（例如：Mac ARM 可能返回 `null`）。
2. `WKWebView` 的 `evaluateJavaScript` 受限（[iOS](/docs/platforms/ios)）。
3. `createWebView()` 在 web 上返回 `null`（[Web](/docs/platforms/web-teavm)）。
4. `Device.homeFolder` 在 web 上为 `null`。
5. web 上的 HTTP 有限制（CORS、无 socket）——`Utils.getRemoteFile` 返回 `null`。

> **可移植性的黄金法则：**任何设备功能在某个目标平台上都可能不存在。使用前请先检查
> （`Device.platform`、`createWebView() != null`、返回值 `null`）——参见
> [原生集成](/docs/platforms/native-integration)。

## 该读哪一页
| 目标 | 页面 |
|---|---|
| 🖥️ 快速迭代 | [桌面端 (LWJGL3)](/docs/platforms/desktop-lwjgl3) |
| 🤖 Android 手机 | [Android](/docs/platforms/android) |
| 🍎 iPhone/iPad | [iOS (RoboVM)](/docs/platforms/ios) |
| 🌐 浏览器 | [Web (TeaVM)](/docs/platforms/web-teavm) |
| 将代码连接到操作系统 | [原生集成](/docs/platforms/native-integration) |
| 打包与发布 | [构建与分发](/docs/platforms/building-and-releasing) |
