名称: dotnet-uno-目标部署 描述: “部署Uno Platform应用程序。针对WASM、iOS、Android、macOS、Windows、Linux和嵌入式平台的具体指导。” 用户可调用: false
dotnet-uno-目标部署
针对Uno Platform应用程序的跨平台部署指南:Web/WASM、iOS、Android、macOS (Catalyst)、Windows、Linux (Skia/GTK) 和嵌入式 (Skia/Framebuffer)。每个目标部分涵盖项目设置、调试工作流、打包/分发、平台特定问题、AOT/修剪影响以及与其他目标的行为差异。
范围
- 各目标项目设置:WASM、iOS、Android、macOS (Catalyst)、Windows、Linux、嵌入式
- 每个目标平台的调试工作流
- 打包和分发(App Store、Play Store、MSIX、Flatpak)
- 平台特定问题和AOT/修剪影响
范围外
- 核心Uno Platform开发(扩展、MVUX、工具包)-- 参见 [技能:dotnet-uno-platform]
- 实时文档的MCP集成 – 参见 [技能:dotnet-uno-mcp]
- Uno Platform测试 – 参见 [技能:dotnet-uno-testing]
- 通用AOT/修剪模式 – 参见 [技能:dotnet-aot-wasm]
- UI框架选择 – 参见 [技能:dotnet-ui-chooser]
交叉引用:核心开发参见 [技能:dotnet-uno-platform],MCP集成参见 [技能:dotnet-uno-mcp],测试参见 [技能:dotnet-uno-testing],通用WASM AOT模式参见 [技能:dotnet-aot-wasm],框架选择参见 [技能:dotnet-ui-chooser]。
目标平台概述
| 目标 | TFM | 工具 | 打包 | 关键限制 |
|---|---|---|---|---|
| Web/WASM | net8.0-browserwasm |
浏览器开发工具 | 静态托管 / Azure SWA | 无文件系统访问,推荐AOT,线程有限 |
| iOS | net8.0-ios |
Xcode / VS Code / Rider | App Store / TestFlight | 配置配置文件,授权,无JIT |
| Android | net8.0-android |
Android SDK / 模拟器 | Play Store / APK侧载 | SDK版本目标,权限 |
| macOS (Catalyst) | net8.0-maccatalyst |
Xcode | Mac App Store / 公证 | 沙箱限制,授权 |
| Windows | net8.0-windows10.0.19041 |
Visual Studio | MSIX / Windows Store | WinAppSDK版本对齐 |
| Linux | net8.0-desktop |
Skia/GTK主机 | AppImage / Flatpak / Snap | GTK依赖,Skia渲染 |
| Embedded | net8.0-desktop |
Skia/Framebuffer | 直接部署 | 无窗口系统,无头渲染 |
TFM备注: 在编程检测平台目标时,使用版本无关的通配符(net*-ios、net*-android),以避免在旧版或新版TFM上出现误报。
Web/WASM
项目设置
# 运行WASM目标
dotnet run -f net8.0-browserwasm --project MyApp/MyApp.csproj
WASM目标在浏览器中渲染XAML控件。渲染器取决于项目配置:Skia(画布/WebGL)或原生HTML映射。应用程序通过JavaScript引导程序(uno-bootstrap.js)加载,初始化.NET WASM运行时。
调试
- 浏览器开发工具: 在Chrome/Edge中使用F12检查DOM、网络、控制台输出
- .NET调试: Visual Studio和VS Code支持通过浏览器CDP调试.NET WASM运行时
- Uno开发服务器:
dotnet run启动一个具有实时重载支持的开发服务器 - 控制台日志:
ILogger输出出现在浏览器控制台中
打包/分发
# 为生产环境发布
dotnet publish -f net8.0-browserwasm -c Release --output ./publish
# 输出是静态站点(HTML/JS/WASM)
# 部署到:Azure静态Web应用、GitHub Pages、Netlify、任何静态主机
发布输出是自包含的静态站点,无需服务器端运行时。
平台问题
- 无文件系统访问: 通过JS互操作或Uno.Storage使用浏览器存储API(IndexedDB、localStorage)
- 线程限制: Web Workers提供有限的多线程;
Task.Run在WASM上可能无法并行化 - CORS限制: WASM中的HTTP请求受浏览器CORS策略约束
- 初始加载时间: .NET WASM运行时和程序集必须下载后应用才可用。使用程序集修剪来减少下载大小。AOT提高运行时执行速度但增加工件大小
- 深度链接: 在Uno导航扩展中配置URL路由,以支持浏览器URL栏导航
AOT/修剪
修剪 通过移除未使用代码减少下载大小。AOT 预编译IL到WebAssembly,提高运行时执行速度但增加工件大小。同时使用两者,并为您的应用衡量权衡。
<PropertyGroup Condition="'$(TargetFramework)' == 'net8.0-browserwasm'">
<WasmShellMonoRuntimeExecutionMode>InterpreterAndAOT</WasmShellMonoRuntimeExecutionMode>
<RunAOTCompilation>true</RunAOTCompilation>
</PropertyGroup>
修剪对WASM至关重要。 未修剪的应用程序可能超过30MB。通过修剪,典型应用程序为5-15MB。AOT增加工件大小但消除运行时的解释器开销——在目标条件下分析下载时间和执行速度。
对于Uno特定的AOT问题(链接器描述符、Uno源代码生成器),参见AOT部分。对于通用WASM AOT模式,参见 [技能:dotnet-aot-wasm]。
行为差异
- 导航: 基于URL的深度链接原生工作;路由映射应定义URL模式
- 认证: OAuth流程使用浏览器重定向(弹出或重定向);无原生浏览器可用。令牌存储使用浏览器安全存储
- 调试: 通过CDP提供完整.NET调试器;断点在Visual Studio/VS Code中工作
- 文件选择器: 使用浏览器文件输入API;
FileOpenPicker映射到<input type="file">
iOS
项目设置
# 为iOS模拟器构建
dotnet build -f net8.0-ios
# 在模拟器上运行
dotnet run -f net8.0-ios
需要在macOS上安装Xcode。渲染器(Skia或原生)取决于项目配置和Uno版本。
调试
- Visual Studio(配对到Mac)/ VS Code + C#开发套件 / Rider: 附加到iOS模拟器或设备
- Xcode Instruments: 分析CPU、内存和能源使用
- 热重载: 通过
DOTNET_MODIFIABLE_ASSEMBLIES=debug支持
打包/分发
# 为App Store发布
dotnet publish -f net8.0-ios -c Release \
/p:CodesignKey="Apple Distribution: MyCompany" \
/p:CodesignProvision="MyApp Distribution Profile"
分发渠道:App Store(需要Apple开发者账户)、TestFlight(beta测试)、Ad Hoc(企业)。
必需: 配置配置文件、签名证书、用于功能(推送通知、HealthKit等)的授权文件。
平台问题
- 无JIT编译: iOS禁止JIT。所有代码必须AOT编译或解释。.NET运行时默认使用Mono解释器
- 配置配置文件: 必须匹配捆绑ID、团队ID和授权。过期的配置文件导致隐晦的构建失败
- 后台执行: iOS限制后台处理。使用
BGTaskScheduler进行后台工作 - 应用传输安全(ATS): 默认需要HTTPS;HTTP需要在
Info.plist中添加NSAppTransportSecurity例外 - 内存压力: iOS积极杀死后台应用。处理
MemoryWarning事件
AOT/修剪
iOS默认需要AOT(无JIT)。.NET运行时编译到原生ARM64代码。
<PropertyGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">
<PublishTrimmed>true</PublishTrimmed>
<TrimMode>link</TrimMode>
</PropertyGroup>
问题: 反射密集型代码在iOS上静默失败。在开发期间启用修剪进行测试,不仅仅是发布构建。
行为差异
- 导航: 基于手势的后退导航(从左侧边缘滑动)通过Uno导航自动实现
- 认证: 使用
ASWebAuthenticationSession进行OAuth;通过LocalAuthentication框架进行生物识别认证 - 调试: 模拟器快速;设备调试需要USB连接和配置。支持热重载的远程调试
Android
项目设置
# 为Android构建
dotnet build -f net8.0-android
# 部署到连接的设备或模拟器
dotnet run -f net8.0-android
需要Android SDK(通过 dotnet workload install android 或Android Studio安装)。
调试
- Android模拟器: 使用Android Studio的模拟器或命令行
emulator - ADB(Android调试桥):
adb logcat用于运行时日志,adb devices验证连接 - Visual Studio / VS Code: 在模拟器或设备上完整调试,带断点
打包/分发
# 发布签名的APK/AAB(使用环境变量或CI密码,切勿硬编码)
dotnet publish -f net8.0-android -c Release \
/p:AndroidKeyStore=true \
/p:AndroidSigningKeyStore=mykey.keystore \
/p:AndroidSigningStorePass="$ANDROID_KEYSTORE_PASS" \
/p:AndroidSigningKeyAlias=myalias \
/p:AndroidSigningKeyPass="$ANDROID_KEY_PASS"
Google Play需要Android应用捆绑包(AAB)格式。侧载使用APK。
平台问题
- SDK版本目标:
<SupportedOSPlatformVersion>控制最低API级别。目标API 34+以符合Google Play要求 - 运行时权限: 危险权限(相机、位置、存储)需要运行时请求。在代码中检查和请求,不仅仅是清单
- Activity生命周期: Android默认在旋转时销毁Activity。Uno处理此问题,但自定义原生代码必须生命周期感知
- ProGuard/R8: 代码缩减可能移除反射目标。对于原生互操作类型,使用
@Keep注解或链接器配置
AOT/修剪
<PropertyGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">
<PublishTrimmed>true</PublishTrimmed>
<RunAOTCompilation>true</RunAOTCompilation>
</PropertyGroup>
Android上的AOT提高启动时间。与iOS不同,JIT作为未修剪代码路径的回退可用。
行为差异
- 导航: 硬件/软件后退按钮触发Uno导航后退。
BackRequested事件自动触发 - 认证: 使用
CustomTabs(Chrome)进行OAuth;通过AndroidX.Biometric进行生物识别认证 - 调试: 模拟器比iOS模拟器慢但支持更多API级别。使用硬件加速(HAXM/KVM)以提高性能
macOS (Catalyst)
项目设置
# 为macOS构建
dotnet build -f net8.0-maccatalyst
# 在macOS上运行
dotnet run -f net8.0-maccatalyst
使用Mac Catalyst(iOS API适配于macOS)。需要在macOS上安装Xcode。
调试
- Visual Studio(配对到Mac)/ VS Code + C#开发套件 / Rider: 原生macOS调试
- Xcode Instruments: 分析性能和内存
- Console.app: 查看应用进程的系统日志
打包/分发
# 为分发发布
dotnet publish -f net8.0-maccatalyst -c Release \
/p:CodesignKey="Developer ID Application: MyCompany" \
/p:CodesignProvision="MyApp Mac Profile"
分发渠道:Mac App Store、带公证的直接下载、企业部署。
需要公证: macOS Gatekeeper要求App Store外的应用进行公证。使用 xcrun notarytool 提交。
平台问题
- 沙箱限制: Mac App Store应用在沙箱中运行。文件访问需要授权和用户授权
- Catalyst限制: 并非所有iOS API都翻译到macOS。检查
SupportedOSPlatform属性 - 菜单栏: macOS期望适当的菜单栏。Uno提供默认菜单;通过平台特定代码自定义
- 窗口管理: macOS应用支持多个窗口。Uno的单窗口模型可能需要适应多窗口场景
AOT/修剪
与iOS相同(Catalyst默认AOT)。建议分发构建进行修剪。
行为差异
- 导航: 无滑动后退手势;依赖工具栏后退按钮或键盘快捷键(Cmd+[)
- 认证: 使用
ASWebAuthenticationSession(与iOS共享);通过Secure Enclave支持Touch ID - 调试: 原生macOS进程;标准.NET调试工具工作。无模拟器——作为原生应用运行
Windows
项目设置
# 为Windows构建
dotnet build -f net8.0-windows10.0.19041
# 在Windows上运行
dotnet run -f net8.0-windows10.0.19041
Windows目标可以使用Skia渲染器或原生WinAppSDK/WinUI 3渲染。
调试
- Visual Studio: 完整调试,带XAML热重载、实时可视化树、实时属性资源管理器
- WinUI诊断: 内置诊断覆盖用于布局检查
打包/分发
# 打包为MSIX
dotnet publish -f net8.0-windows10.0.19041 -c Release \
/p:PackageOutputPath=./packages
分发:Microsoft Store(MSIX)、带证书的侧载、ClickOnce或直接EXE。
平台问题
- WinAppSDK版本对齐: Windows TFM版本必须匹配最低Windows版本。
10.0.19041= Windows 10 2004+ - UAC和提升: 应用无法自我提升。为标准用户权限设计
- Windows特定API:
Windows.Storage、Windows.NetworkingAPI仅在Windows目标上可用。使用条件编译或Uno抽象 - MSIX签名: MSIX包必须签名才能安装。使用代码签名证书进行分发
AOT/修剪
<PropertyGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">
<PublishTrimmed>true</PublishTrimmed>
<PublishAot>true</PublishAot>
</PropertyGroup>
Windows支持JIT和AOT。AOT产生单个原生EXE,启动更快。
行为差异
- 导航: 标准Windows导航(Alt+Left后退,标题栏后退按钮)
- 认证: 使用系统浏览器或WAM(Web账户管理器)进行Microsoft账户的SSO
- 调试: 最丰富的调试体验,带Visual Studio实时可视化树和XAML热重载
Linux (Skia/GTK)
项目设置
# 为Linux桌面构建
dotnet build -f net8.0-desktop
# 在Linux上运行
dotnet run -f net8.0-desktop
Linux目标使用Skia渲染器与GTK主机窗口。所有XAML渲染由Skia完成——GTK仅提供窗口和输入处理。
调试
- VS Code带C#开发套件: Linux上的远程调试
- JetBrains Rider: 完整Linux调试支持
- 控制台日志:
dotnet run输出日志到终端
打包/分发
# 为Linux发布自包含
dotnet publish -f net8.0-desktop -c Release \
--self-contained \
-r linux-x64
# 打包为AppImage、Flatpak或Snap
分发:AppImage(便携,无需安装)、Flatpak(沙箱化)、Snap(Ubuntu商店)、DEB/RPM包。
平台问题
- GTK依赖: 应用在运行时需要GTK3库。打包或记录依赖:
libgtk-3-0、libskia* - Skia渲染: 所有渲染基于Skia。不使用原生GTK小部件。这确保跨平台渲染的像素完美,但意味着应用不遵循主机GTK主题
- 字体渲染: 确保目标系统上字体可用。在应用中嵌入字体或声明字体依赖
- 显示缩放: 通过Skia支持HiDPI;使用环境变量
GDK_SCALE=2测试
AOT/修剪
<PropertyGroup Condition="'$(TargetFramework)' == 'net8.0-desktop'">
<PublishTrimmed>true</PublishTrimmed>
<PublishAot>true</PublishAot>
</PropertyGroup>
Linux上的AOT产生原生二进制文件。自包含部署避免需要系统级.NET运行时。
行为差异
- 导航: 键盘驱动(Alt+Left后退)。无基于手势的导航
- 认证: 使用系统浏览器进行OAuth流程。无平台特定认证API
- 调试: 标准.NET调试。无平台特定调试器工具
Embedded (Skia/Framebuffer)
项目设置
嵌入式目标使用Skia渲染器与framebuffer后端,实现无窗口系统或无头渲染。
# 为嵌入式构建(与桌面相同TFM)
dotnet build -f net8.0-desktop -r linux-arm64
# 直接在嵌入式设备上运行
dotnet run -f net8.0-desktop
嵌入式目标与Linux桌面共享 net8.0-desktop TFM。平台特定配置选择framebuffer后端。
// Program.cs -- framebuffer主机配置
public static void Main(string[] args)
{
SkiaHostBuilder.Create()
.UseFrameBuffer() // 使用framebuffer代替GTK
.App(() => new App())
.Build()
.Run();
}
调试
- SSH远程调试: 使用VS Code Remote或
dotnet-trace进行远程性能分析 - 串行控制台: 将日志重定向到串行输出,用于无头调试
- 远程日志: 配置带网络接收器的Serilog,用于远程日志聚合
打包/分发
# 为ARM64发布自包含
dotnet publish -f net8.0-desktop -c Release \
--self-contained \
-r linux-arm64
# 直接部署二进制文件到设备文件系统
scp -r ./publish/* pi@device:/opt/myapp/
直接部署到设备文件系统。无应用商店或包管理器。
平台问题
- 无窗口系统: 无X11/Wayland。应用直接渲染到Linux framebuffer(
/dev/fb0) - 输入处理: 通过Linux输入事件(
/dev/input/event*)进行触摸输入。默认无鼠标光标 - 资源有限: 嵌入式设备通常有受限的RAM和CPU。仔细分析内存使用
- 无浏览器: 需要浏览器的OAuth流程不可用。使用设备码流或预配置令牌
- 显示分辨率: 必须匹配framebuffer分辨率。通过内核启动参数或
fbset设置
AOT/修剪
强烈推荐嵌入式使用AOT,由于资源有限和启动时间要求。
<PropertyGroup Condition="'$(RuntimeIdentifier)' == 'linux-arm64'">
<PublishTrimmed>true</PublishTrimmed>
<PublishAot>true</PublishAot>
<InvariantGlobalization>true</InvariantGlobalization>
</PropertyGroup>
InvariantGlobalization 通过移除ICU数据减少二进制大小(约28MB)。仅当应用不需要区域特定格式时使用。
行为差异
- 导航: 仅触摸或硬件按钮。无键盘快捷键或手势栏
- 认证: 设备码流或预配置凭据。无基于浏览器的OAuth
- 调试: 仅远程。无本地IDE。使用SSH调试或远程日志
跨目标行为差异
导航
| 目标 | 后退导航 | 深度链接 | 手势导航 |
|---|---|---|---|
| Web/WASM | 浏览器后退按钮 / Alt+Left | 基于URL的路由 | 无 |
| iOS | 从左侧边缘滑动 | URL方案 / 通用链接 | 完整手势支持 |
| Android | 硬件/软件后退按钮 | 意图过滤器 / 应用链接 | 系统后退手势(Android 13+) |
| macOS | Cmd+[ / 工具栏后退 | URL方案 | 无 |
| Windows | Alt+Left / 标题栏后退 | 协议激活 | 无 |
| Linux | Alt+Left / 键盘 | 无 | 无 |
| Embedded | 仅硬件按钮 | 无 | 仅触摸 |
认证
| 目标 | OAuth流程 | 令牌存储 | 生物识别 |
|---|---|---|---|
| Web/WASM | 浏览器重定向/弹出 | 浏览器安全存储 | WebAuthn(有限) |
| iOS | ASWebAuthenticationSession |
Keychain | Touch ID / Face ID |
| Android | Chrome Custom Tabs | Android Keystore | BiometricPrompt |
| macOS | ASWebAuthenticationSession |
Keychain | Touch ID |
| Windows | 系统浏览器 / WAM | 凭据管理器 | Windows Hello |
| Linux | 系统浏览器 | 秘密服务API | 无 |
| Embedded | 设备码流 | 基于文件(加密) | 无 |
调试工具
| 目标 | IDE调试器 | 热重载 | 性能分析 |
|---|---|---|---|
| Web/WASM | VS / VS Code(CDP) | 是 | 浏览器开发工具 |
| iOS | VS(配对到Mac)/ VS Code / Rider | 是 | Xcode Instruments |
| Android | VS / VS Code | 是 | Android性能分析器 |
| macOS | VS(配对到Mac)/ VS Code / Rider | 是 | Xcode Instruments |
| Windows | Visual Studio | 是(+ 实时可视化树) | VS诊断工具 |
| Linux | VS Code / Rider | 是 | dotnet-counters / dotnet-trace |
| Embedded | VS Code Remote | 是(SSH) | dotnet-trace(远程) |
代理问题
- 不要在检测逻辑中硬编码TFM版本。 使用版本无关的通配符(
net*-ios、net*-android)以处理.NET 8和未来.NET版本。 - 不要假设所有目标共享相同的调试工作流。 iOS需要配置;Android需要ADB;WASM使用浏览器开发工具。每个目标有独特的工具。
- 不要在iOS上使用依赖JIT的模式。 iOS禁止JIT编译。使用
Reflection.Emit、Expression.Compile()或动态程序集加载的代码将在运行时失败。 - 不要忘记平台特定权限。 Android运行时权限、iOS授权、macOS沙箱权限和WASM CORS策略都不同,必须按目标处理。
- 不要部署未修剪的WASM应用。 未修剪的WASM捆绑包超过30MB。始终启用修剪,并考虑生产WASM部署使用AOT。
- 不要假设所有目标都有文件系统访问。 WASM无文件系统;iOS/macOS有沙箱限制;嵌入式可能有只读存储。使用Uno存储抽象。
- 不要对所有目标使用相同的认证流程。 WASM使用浏览器重定向,移动使用原生认证会话,嵌入式使用设备码流。认证必须按目标配置。
先决条件
- .NET 8.0+ 带平台工作负载:
dotnet workload install ios android maccatalyst wasm-tools - 平台SDK:Xcode(iOS/macOS)、Android SDK(Android)、GTK3(Linux)
- Uno Platform 5.x+