找回密码
 立即注册
首页 业界区 业界 AOT编译Avalonia应用:StarBlog Publisher项目实践与挑 ...

AOT编译Avalonia应用:StarBlog Publisher项目实践与挑战

战匈琼 4 天前
前言

最近我使用 Avalonia 开发了一个文章发布工具,StarBlog Publisher。
Avalonia 是一个跨平台的 UI 框架,它可以在 Windows、Linux 和 macOS 上运行。它的特点是高性能、跨平台、易于使用。
Avalonia 有很多优点,比如高性能、跨平台、易于使用。但是,它也有一些缺点,比如学习曲线较陡峭、文档较难找到。
但 Avalonia 是基于 .NetCore 框架开发的,最终打包出来的可执行文件,如果选择 framework-dependant 发布,那么需要在客户端上安装 .NetCore 运行时环境,这对用户来说是一个很大的负担。如果使用 self-contained 发布,体积又比较大。
并且还容易被反编译,这在一些商业软件中是不允许的。(不过我这个项目是开源的,所以没有这个问题)
本文以 StarBlog Publisher 项目为例,记录一下使用 AOT 发布 Avalonia 应用的踩坑过程。
新的1.1版本已经发布,欢迎下载尝试: https://github.com/star-blog/starblog-publisher/releases
关于 AOT

从 .Net7 开始,逐步开始支持 AOT 发布,这是一个非常重要的特性。AOT 发布可以将 .Net 应用程序编译成不依赖运行库的机器码,体积较小,而且不容易被反编译。
Native AOT = 编译时把 .NET 程序的 IL(中间语言)直接提前编译成机器码,生成真正的、单独的、没有依赖 CLR 的可执行文件。
相比传统 .NET 应用在运行时用 JIT(即时编译器)动态编译,Native AOT是 提前(Ahead of Time) 直接搞定所有编译工作。
以下是一个简单的 Native AOT 流程图
  1. C# 代码
  2.    ↓
  3. Roslyn 编译(生成 IL)
  4.    ↓
  5. Native AOT 编译器 (ilc)
  6.    ↓
  7. - 静态分析
  8. - Tree Shaking
  9. - IL到机器码转换
  10. - 链接器合成可执行文件
  11.    ↓
  12. 最终单个 .exe (无JIT、无大型CLR)
复制代码
目前的 LTS 版本是 .Net8,对 AOT 的支持已经比较完善了,这次我来尝试使用 AOT 方式发布 Avalonia 应用。
PS:据说 .Net9 对 AOT 方式提供了很多优化和改进,接下来我会尝试一下。
使用 AOT 可能会遇到的问题


  • 兼容性问题 :AOT编译可能与某些依赖库不兼容,特别是那些依赖反射、动态代码生成或JIT编译的库。如果遇到问题,可能需要在rd.xml中添加更多配置。
  • 包大小 :AOT编译会生成更大的可执行文件(相比起 framework-dependant 模式而言),但启动速度更快。
  • 调试困难 :AOT编译的应用程序调试可能更加困难。
  • 第三方库 :检查项目中使用的第三方库是否支持AOT编译。例如, Microsoft.Extensions.AI 和 Microsoft.Extensions.AI.OpenAI 是预览版,可能需要特别注意其AOT兼容性。
  • Avalonia特定配置 :对于Avalonia应用,可能需要确保XAML相关的类型信息被正确保留。
修改项目文件

首先需要在项目文件中添加AOT相关的配置:
  1. <Project Sdk="Microsoft.NET.Sdk">
  2. <ItemGroup>
  3.     <RdXmlFile Include="rd.xml" />
  4. </ItemGroup><PropertyGroup>
  5. <ItemGroup>
  6.     <RdXmlFile Include="rd.xml" />
  7. </ItemGroup><ItemGroup>
  8.     <RdXmlFile Include="rd.xml" />
  9. </ItemGroup><OutputType>WinExe</OutputType>
  10. <ItemGroup>
  11.     <RdXmlFile Include="rd.xml" />
  12. </ItemGroup><ItemGroup>
  13.     <RdXmlFile Include="rd.xml" />
  14. </ItemGroup><TargetFramework>net8.0</TargetFramework>
  15. <ItemGroup>
  16.     <RdXmlFile Include="rd.xml" />
  17. </ItemGroup><ItemGroup>
  18.     <RdXmlFile Include="rd.xml" />
  19. </ItemGroup><Nullable>enable</Nullable>
  20. <ItemGroup>
  21.     <RdXmlFile Include="rd.xml" />
  22. </ItemGroup><ItemGroup>
  23.     <RdXmlFile Include="rd.xml" />
  24. </ItemGroup><BuiltInComInteropSupport>true</BuiltInComInteropSupport>
  25. <ItemGroup>
  26.     <RdXmlFile Include="rd.xml" />
  27. </ItemGroup><ItemGroup>
  28.     <RdXmlFile Include="rd.xml" />
  29. </ItemGroup>app.manifest</ApplicationManifest>
  30. <ItemGroup>
  31.     <RdXmlFile Include="rd.xml" />
  32. </ItemGroup><ItemGroup>
  33.     <RdXmlFile Include="rd.xml" />
  34. </ItemGroup>true</AvaloniaUseCompiledBindingsByDefault>
  35. <ItemGroup>
  36.     <RdXmlFile Include="rd.xml" />
  37. </ItemGroup><ItemGroup>
  38.     <RdXmlFile Include="rd.xml" />
  39. </ItemGroup>
  40. <ItemGroup>
  41.     <RdXmlFile Include="rd.xml" />
  42. </ItemGroup><ItemGroup>
  43.     <RdXmlFile Include="rd.xml" />
  44. </ItemGroup>
  45. <ItemGroup>
  46.     <RdXmlFile Include="rd.xml" />
  47. </ItemGroup><ItemGroup>
  48.     <RdXmlFile Include="rd.xml" />
  49. </ItemGroup><PublishAot>true</PublishAot>
  50. <ItemGroup>
  51.     <RdXmlFile Include="rd.xml" />
  52. </ItemGroup><ItemGroup>
  53.     <RdXmlFile Include="rd.xml" />
  54. </ItemGroup><TrimMode>full</TrimMode>
  55. <ItemGroup>
  56.     <RdXmlFile Include="rd.xml" />
  57. </ItemGroup><ItemGroup>
  58.     <RdXmlFile Include="rd.xml" />
  59. </ItemGroup><InvariantGlobalization>true</InvariantGlobalization>
  60. <ItemGroup>
  61.     <RdXmlFile Include="rd.xml" />
  62. </ItemGroup><ItemGroup>
  63.     <RdXmlFile Include="rd.xml" />
  64. </ItemGroup><IlcGenerateStackTraceData>false</IlcGenerateStackTraceData>
  65. <ItemGroup>
  66.     <RdXmlFile Include="rd.xml" />
  67. </ItemGroup><ItemGroup>
  68.     <RdXmlFile Include="rd.xml" />
  69. </ItemGroup><IlcOptimizationPreference>Size</IlcOptimizationPreference>
  70. <ItemGroup>
  71.     <RdXmlFile Include="rd.xml" />
  72. </ItemGroup><ItemGroup>
  73.     <RdXmlFile Include="rd.xml" />
  74. </ItemGroup><IlcFoldIdenticalMethodBodies>true</IlcFoldIdenticalMethodBodies>
  75. <ItemGroup>
  76.     <RdXmlFile Include="rd.xml" />
  77. </ItemGroup><ItemGroup>
  78.     <RdXmlFile Include="rd.xml" />
  79. </ItemGroup>
  80. <ItemGroup>
  81.     <RdXmlFile Include="rd.xml" />
  82. </ItemGroup><ItemGroup>
  83.     <RdXmlFile Include="rd.xml" />
  84. </ItemGroup><JsonSerializerIsReflectionEnabledByDefault>true</JsonSerializerIsReflectionEnabledByDefault>
  85. <ItemGroup>
  86.     <RdXmlFile Include="rd.xml" />
  87. </ItemGroup></PropertyGroup>
  88. <ItemGroup>
  89.     <RdXmlFile Include="rd.xml" />
  90. </ItemGroup>
  91. </Project>
复制代码
JSON序列化问题

在AOT编译环境中,JSON序列化是一个常见的问题点,因为它通常依赖于运行时反射。
这个项目有几个地方用到了 JSON
一个是应用设置,另一个是网络请求
先说结论:Newtonsoft.Json 相比 System.Text.Json 对 AOT 的支持更好,如果要使用 AOT,优先使用 Newtonsoft.Json 库。
修改应用设置 AppSettings.cs 支持AOT

如果非要使用  System.Text.Json ,那么需要修改一下。用 Newtonsoft.Json 的话直接跳过。
  1. using System;
  2. using System.IO;
  3. using System.Text.Json;
  4. using System.Text.Json.Serialization;
  5. using StarBlogPublisher.Services.Security;
  6. using System.Text.Json.Serialization.Metadata; // 添加此命名空间
  7. namespace StarBlogPublisher.Services;
  8. // 添加JsonSerializable特性,为AOT生成序列化代码
  9. [JsonSerializable(typeof(AppSettings))]
  10. internal partial class AppSettingsContext : JsonSerializerContext
  11. {
  12. }
  13. public class AppSettings {
  14. <ItemGroup>
  15.     <RdXmlFile Include="rd.xml" />
  16. </ItemGroup>private static readonly string ConfigPath = Path.Combine(
  17. <ItemGroup>
  18.     <RdXmlFile Include="rd.xml" />
  19. </ItemGroup><ItemGroup>
  20.     <RdXmlFile Include="rd.xml" />
  21. </ItemGroup>Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
  22. <ItemGroup>
  23.     <RdXmlFile Include="rd.xml" />
  24. </ItemGroup><ItemGroup>
  25.     <RdXmlFile Include="rd.xml" />
  26. </ItemGroup>"StarBlogPublisher",
  27. <ItemGroup>
  28.     <RdXmlFile Include="rd.xml" />
  29. </ItemGroup><ItemGroup>
  30.     <RdXmlFile Include="rd.xml" />
  31. </ItemGroup>"settings.json"
  32. <ItemGroup>
  33.     <RdXmlFile Include="rd.xml" />
  34. </ItemGroup>);
  35. <ItemGroup>
  36.     <RdXmlFile Include="rd.xml" />
  37. </ItemGroup>// ... 现有代码 ...
  38. <ItemGroup>
  39.     <RdXmlFile Include="rd.xml" />
  40. </ItemGroup>private static AppSettings Load() {
  41. <ItemGroup>
  42.     <RdXmlFile Include="rd.xml" />
  43. </ItemGroup><ItemGroup>
  44.     <RdXmlFile Include="rd.xml" />
  45. </ItemGroup>try {
  46. <ItemGroup>
  47.     <RdXmlFile Include="rd.xml" />
  48. </ItemGroup><ItemGroup>
  49.     <RdXmlFile Include="rd.xml" />
  50. </ItemGroup><ItemGroup>
  51.     <RdXmlFile Include="rd.xml" />
  52. </ItemGroup>if (File.Exists(ConfigPath)) {
  53. <ItemGroup>
  54.     <RdXmlFile Include="rd.xml" />
  55. </ItemGroup><ItemGroup>
  56.     <RdXmlFile Include="rd.xml" />
  57. </ItemGroup><ItemGroup>
  58.     <RdXmlFile Include="rd.xml" />
  59. </ItemGroup><ItemGroup>
  60.     <RdXmlFile Include="rd.xml" />
  61. </ItemGroup>var json = File.ReadAllText(ConfigPath);
  62. <ItemGroup>
  63.     <RdXmlFile Include="rd.xml" />
  64. </ItemGroup><ItemGroup>
  65.     <RdXmlFile Include="rd.xml" />
  66. </ItemGroup><ItemGroup>
  67.     <RdXmlFile Include="rd.xml" />
  68. </ItemGroup><ItemGroup>
  69.     <RdXmlFile Include="rd.xml" />
  70. </ItemGroup>// 使用AOT友好的序列化方式
  71. <ItemGroup>
  72.     <RdXmlFile Include="rd.xml" />
  73. </ItemGroup><ItemGroup>
  74.     <RdXmlFile Include="rd.xml" />
  75. </ItemGroup><ItemGroup>
  76.     <RdXmlFile Include="rd.xml" />
  77. </ItemGroup><ItemGroup>
  78.     <RdXmlFile Include="rd.xml" />
  79. </ItemGroup>var settings = JsonSerializer.Deserialize(json, AppSettingsContext.Default.AppSettings);
  80. <ItemGroup>
  81.     <RdXmlFile Include="rd.xml" />
  82. </ItemGroup><ItemGroup>
  83.     <RdXmlFile Include="rd.xml" />
  84. </ItemGroup><ItemGroup>
  85.     <RdXmlFile Include="rd.xml" />
  86. </ItemGroup><ItemGroup>
  87.     <RdXmlFile Include="rd.xml" />
  88. </ItemGroup>return settings ?? new AppSettings();
  89. <ItemGroup>
  90.     <RdXmlFile Include="rd.xml" />
  91. </ItemGroup><ItemGroup>
  92.     <RdXmlFile Include="rd.xml" />
  93. </ItemGroup><ItemGroup>
  94.     <RdXmlFile Include="rd.xml" />
  95. </ItemGroup>}
  96. <ItemGroup>
  97.     <RdXmlFile Include="rd.xml" />
  98. </ItemGroup><ItemGroup>
  99.     <RdXmlFile Include="rd.xml" />
  100. </ItemGroup>}
  101. <ItemGroup>
  102.     <RdXmlFile Include="rd.xml" />
  103. </ItemGroup><ItemGroup>
  104.     <RdXmlFile Include="rd.xml" />
  105. </ItemGroup>catch (Exception ex) {
  106. <ItemGroup>
  107.     <RdXmlFile Include="rd.xml" />
  108. </ItemGroup><ItemGroup>
  109.     <RdXmlFile Include="rd.xml" />
  110. </ItemGroup><ItemGroup>
  111.     <RdXmlFile Include="rd.xml" />
  112. </ItemGroup>// 如果加载失败,返回默认设置
  113. <ItemGroup>
  114.     <RdXmlFile Include="rd.xml" />
  115. </ItemGroup><ItemGroup>
  116.     <RdXmlFile Include="rd.xml" />
  117. </ItemGroup><ItemGroup>
  118.     <RdXmlFile Include="rd.xml" />
  119. </ItemGroup>Console.WriteLine($"Failed to load app settings. {ex}");
  120. <ItemGroup>
  121.     <RdXmlFile Include="rd.xml" />
  122. </ItemGroup><ItemGroup>
  123.     <RdXmlFile Include="rd.xml" />
  124. </ItemGroup>}
  125. <ItemGroup>
  126.     <RdXmlFile Include="rd.xml" />
  127. </ItemGroup><ItemGroup>
  128.     <RdXmlFile Include="rd.xml" />
  129. </ItemGroup>return new AppSettings();
  130. <ItemGroup>
  131.     <RdXmlFile Include="rd.xml" />
  132. </ItemGroup>}
  133. <ItemGroup>
  134.     <RdXmlFile Include="rd.xml" />
  135. </ItemGroup>public void Save() {
  136. <ItemGroup>
  137.     <RdXmlFile Include="rd.xml" />
  138. </ItemGroup><ItemGroup>
  139.     <RdXmlFile Include="rd.xml" />
  140. </ItemGroup>try {
  141. <ItemGroup>
  142.     <RdXmlFile Include="rd.xml" />
  143. </ItemGroup><ItemGroup>
  144.     <RdXmlFile Include="rd.xml" />
  145. </ItemGroup><ItemGroup>
  146.     <RdXmlFile Include="rd.xml" />
  147. </ItemGroup>var directory = Path.GetDirectoryName(ConfigPath);
  148. <ItemGroup>
  149.     <RdXmlFile Include="rd.xml" />
  150. </ItemGroup><ItemGroup>
  151.     <RdXmlFile Include="rd.xml" />
  152. </ItemGroup><ItemGroup>
  153.     <RdXmlFile Include="rd.xml" />
  154. </ItemGroup>if (!string.IsNullOrEmpty(directory)) {
  155. <ItemGroup>
  156.     <RdXmlFile Include="rd.xml" />
  157. </ItemGroup><ItemGroup>
  158.     <RdXmlFile Include="rd.xml" />
  159. </ItemGroup><ItemGroup>
  160.     <RdXmlFile Include="rd.xml" />
  161. </ItemGroup><ItemGroup>
  162.     <RdXmlFile Include="rd.xml" />
  163. </ItemGroup>Directory.CreateDirectory(directory);
  164. <ItemGroup>
  165.     <RdXmlFile Include="rd.xml" />
  166. </ItemGroup><ItemGroup>
  167.     <RdXmlFile Include="rd.xml" />
  168. </ItemGroup><ItemGroup>
  169.     <RdXmlFile Include="rd.xml" />
  170. </ItemGroup>}
  171. <ItemGroup>
  172.     <RdXmlFile Include="rd.xml" />
  173. </ItemGroup><ItemGroup>
  174.     <RdXmlFile Include="rd.xml" />
  175. </ItemGroup><ItemGroup>
  176.     <RdXmlFile Include="rd.xml" />
  177. </ItemGroup>// 使用AOT友好的序列化方式
  178. <ItemGroup>
  179.     <RdXmlFile Include="rd.xml" />
  180. </ItemGroup><ItemGroup>
  181.     <RdXmlFile Include="rd.xml" />
  182. </ItemGroup><ItemGroup>
  183.     <RdXmlFile Include="rd.xml" />
  184. </ItemGroup>var json = JsonSerializer.Serialize(this, AppSettingsContext.Default.AppSettings, new JsonSerializerOptions {
  185. <ItemGroup>
  186.     <RdXmlFile Include="rd.xml" />
  187. </ItemGroup><ItemGroup>
  188.     <RdXmlFile Include="rd.xml" />
  189. </ItemGroup><ItemGroup>
  190.     <RdXmlFile Include="rd.xml" />
  191. </ItemGroup><ItemGroup>
  192.     <RdXmlFile Include="rd.xml" />
  193. </ItemGroup>WriteIndented = true
  194. <ItemGroup>
  195.     <RdXmlFile Include="rd.xml" />
  196. </ItemGroup><ItemGroup>
  197.     <RdXmlFile Include="rd.xml" />
  198. </ItemGroup><ItemGroup>
  199.     <RdXmlFile Include="rd.xml" />
  200. </ItemGroup>});
  201. <ItemGroup>
  202.     <RdXmlFile Include="rd.xml" />
  203. </ItemGroup><ItemGroup>
  204.     <RdXmlFile Include="rd.xml" />
  205. </ItemGroup><ItemGroup>
  206.     <RdXmlFile Include="rd.xml" />
  207. </ItemGroup>File.WriteAllText(ConfigPath, json);
  208. <ItemGroup>
  209.     <RdXmlFile Include="rd.xml" />
  210. </ItemGroup><ItemGroup>
  211.     <RdXmlFile Include="rd.xml" />
  212. </ItemGroup><ItemGroup>
  213.     <RdXmlFile Include="rd.xml" />
  214. </ItemGroup>// 触发配置变更事件
  215. <ItemGroup>
  216.     <RdXmlFile Include="rd.xml" />
  217. </ItemGroup><ItemGroup>
  218.     <RdXmlFile Include="rd.xml" />
  219. </ItemGroup><ItemGroup>
  220.     <RdXmlFile Include="rd.xml" />
  221. </ItemGroup>SettingsChanged?.Invoke(this, EventArgs.Empty);
  222. <ItemGroup>
  223.     <RdXmlFile Include="rd.xml" />
  224. </ItemGroup><ItemGroup>
  225.     <RdXmlFile Include="rd.xml" />
  226. </ItemGroup>}
  227. <ItemGroup>
  228.     <RdXmlFile Include="rd.xml" />
  229. </ItemGroup><ItemGroup>
  230.     <RdXmlFile Include="rd.xml" />
  231. </ItemGroup>catch (Exception) {
  232. <ItemGroup>
  233.     <RdXmlFile Include="rd.xml" />
  234. </ItemGroup><ItemGroup>
  235.     <RdXmlFile Include="rd.xml" />
  236. </ItemGroup><ItemGroup>
  237.     <RdXmlFile Include="rd.xml" />
  238. </ItemGroup>// todo 处理保存失败的情况
  239. <ItemGroup>
  240.     <RdXmlFile Include="rd.xml" />
  241. </ItemGroup><ItemGroup>
  242.     <RdXmlFile Include="rd.xml" />
  243. </ItemGroup>}
  244. <ItemGroup>
  245.     <RdXmlFile Include="rd.xml" />
  246. </ItemGroup>}
  247. }
复制代码
解释


  • 添加了 [JsonSerializable] 特性和 JsonSerializerContext 派生类,这是.NET中支持AOT的JSON序列化的关键。这会在编译时生成序列化代码,而不是依赖运行时反射。
  • 修改了 Load() 和 Save() 方法,使用 AppSettingsContext.Default.AppSettings 作为类型信息,而不是依赖运行时类型推断。
  • 这种方法确保了在AOT环境中,所有需要的序列化代码都会在编译时生成,而不需要运行时反射。
此外,还需要在项目文件中确保已启用AOT编译的JSON源生成器:
  1. <PropertyGroup>
  2. <ItemGroup>
  3.     <RdXmlFile Include="rd.xml" />
  4. </ItemGroup>
  5. <ItemGroup>
  6.     <RdXmlFile Include="rd.xml" />
  7. </ItemGroup><JsonSerializerIsReflectionEnabledByDefault>false</JsonSerializerIsReflectionEnabledByDefault>
  8. </PropertyGroup>
复制代码
这些修改将确保AppSettings类在AOT编译环境中能够正确地进行JSON序列化和反序列化。
Refit在AOT模式下的JSON序列化问题

在AOT模式下,Refit库的JSON处理也可以使用 Newtonsoft.Json
先安装 Refit.Newtonsoft.Json 库,并且需要额外配置来处理类型信息。
添加类型预注册

需要创建一个新的类来预注册所有API接口中使用的类型:
  1. using Newtonsoft.Json;
  2. using StarBlogPublisher.Models;
  3. using System.Collections.Generic;
  4. using CodeLab.Share.ViewModels.Response;
  5. namespace StarBlogPublisher.Services;
  6. /// <summary>
  7. /// 为AOT编译预注册Refit使用的类型
  8. /// </summary>
  9. public static class RefitTypeRegistration
  10. {
  11. <ItemGroup>
  12.     <RdXmlFile Include="rd.xml" />
  13. </ItemGroup>/// <summary>
  14. <ItemGroup>
  15.     <RdXmlFile Include="rd.xml" />
  16. </ItemGroup>/// 在应用启动时调用此方法,确保所有类型都被预注册
  17. <ItemGroup>
  18.     <RdXmlFile Include="rd.xml" />
  19. </ItemGroup>/// </summary>
  20. <ItemGroup>
  21.     <RdXmlFile Include="rd.xml" />
  22. </ItemGroup>public static void RegisterTypes()
  23. <ItemGroup>
  24.     <RdXmlFile Include="rd.xml" />
  25. </ItemGroup>{
  26. <ItemGroup>
  27.     <RdXmlFile Include="rd.xml" />
  28. </ItemGroup><ItemGroup>
  29.     <RdXmlFile Include="rd.xml" />
  30. </ItemGroup>// 注册常用的响应类型
  31. <ItemGroup>
  32.     <RdXmlFile Include="rd.xml" />
  33. </ItemGroup><ItemGroup>
  34.     <RdXmlFile Include="rd.xml" />
  35. </ItemGroup>JsonConvert.DefaultSettings = () => new JsonSerializerSettings
  36. <ItemGroup>
  37.     <RdXmlFile Include="rd.xml" />
  38. </ItemGroup><ItemGroup>
  39.     <RdXmlFile Include="rd.xml" />
  40. </ItemGroup>{
  41. <ItemGroup>
  42.     <RdXmlFile Include="rd.xml" />
  43. </ItemGroup><ItemGroup>
  44.     <RdXmlFile Include="rd.xml" />
  45. </ItemGroup><ItemGroup>
  46.     <RdXmlFile Include="rd.xml" />
  47. </ItemGroup>TypeNameHandling = TypeNameHandling.Auto,
  48. <ItemGroup>
  49.     <RdXmlFile Include="rd.xml" />
  50. </ItemGroup><ItemGroup>
  51.     <RdXmlFile Include="rd.xml" />
  52. </ItemGroup><ItemGroup>
  53.     <RdXmlFile Include="rd.xml" />
  54. </ItemGroup>// 添加自定义转换器如果需要
  55. <ItemGroup>
  56.     <RdXmlFile Include="rd.xml" />
  57. </ItemGroup><ItemGroup>
  58.     <RdXmlFile Include="rd.xml" />
  59. </ItemGroup><ItemGroup>
  60.     <RdXmlFile Include="rd.xml" />
  61. </ItemGroup>Converters = new List<JsonConverter>
  62. <ItemGroup>
  63.     <RdXmlFile Include="rd.xml" />
  64. </ItemGroup><ItemGroup>
  65.     <RdXmlFile Include="rd.xml" />
  66. </ItemGroup><ItemGroup>
  67.     <RdXmlFile Include="rd.xml" />
  68. </ItemGroup>{
  69. <ItemGroup>
  70.     <RdXmlFile Include="rd.xml" />
  71. </ItemGroup><ItemGroup>
  72.     <RdXmlFile Include="rd.xml" />
  73. </ItemGroup><ItemGroup>
  74.     <RdXmlFile Include="rd.xml" />
  75. </ItemGroup><ItemGroup>
  76.     <RdXmlFile Include="rd.xml" />
  77. </ItemGroup>// 可以添加自定义转换器
  78. <ItemGroup>
  79.     <RdXmlFile Include="rd.xml" />
  80. </ItemGroup><ItemGroup>
  81.     <RdXmlFile Include="rd.xml" />
  82. </ItemGroup><ItemGroup>
  83.     <RdXmlFile Include="rd.xml" />
  84. </ItemGroup>}
  85. <ItemGroup>
  86.     <RdXmlFile Include="rd.xml" />
  87. </ItemGroup><ItemGroup>
  88.     <RdXmlFile Include="rd.xml" />
  89. </ItemGroup>};
  90. <ItemGroup>
  91.     <RdXmlFile Include="rd.xml" />
  92. </ItemGroup><ItemGroup>
  93.     <RdXmlFile Include="rd.xml" />
  94. </ItemGroup>// 预热类型 - 确保这些类型在AOT编译时被包含
  95. <ItemGroup>
  96.     <RdXmlFile Include="rd.xml" />
  97. </ItemGroup><ItemGroup>
  98.     <RdXmlFile Include="rd.xml" />
  99. </ItemGroup>var types = new[]
  100. <ItemGroup>
  101.     <RdXmlFile Include="rd.xml" />
  102. </ItemGroup><ItemGroup>
  103.     <RdXmlFile Include="rd.xml" />
  104. </ItemGroup>{
  105. <ItemGroup>
  106.     <RdXmlFile Include="rd.xml" />
  107. </ItemGroup><ItemGroup>
  108.     <RdXmlFile Include="rd.xml" />
  109. </ItemGroup><ItemGroup>
  110.     <RdXmlFile Include="rd.xml" />
  111. </ItemGroup>typeof(ApiResponse<>),
  112. <ItemGroup>
  113.     <RdXmlFile Include="rd.xml" />
  114. </ItemGroup><ItemGroup>
  115.     <RdXmlFile Include="rd.xml" />
  116. </ItemGroup><ItemGroup>
  117.     <RdXmlFile Include="rd.xml" />
  118. </ItemGroup>typeof(ApiResponse<List<Category>>),
  119. <ItemGroup>
  120.     <RdXmlFile Include="rd.xml" />
  121. </ItemGroup><ItemGroup>
  122.     <RdXmlFile Include="rd.xml" />
  123. </ItemGroup><ItemGroup>
  124.     <RdXmlFile Include="rd.xml" />
  125. </ItemGroup>typeof(ApiResponse<List<WordCloud>>),
  126. <ItemGroup>
  127.     <RdXmlFile Include="rd.xml" />
  128. </ItemGroup><ItemGroup>
  129.     <RdXmlFile Include="rd.xml" />
  130. </ItemGroup><ItemGroup>
  131.     <RdXmlFile Include="rd.xml" />
  132. </ItemGroup>// 添加其他API响应类型
  133. <ItemGroup>
  134.     <RdXmlFile Include="rd.xml" />
  135. </ItemGroup><ItemGroup>
  136.     <RdXmlFile Include="rd.xml" />
  137. </ItemGroup><ItemGroup>
  138.     <RdXmlFile Include="rd.xml" />
  139. </ItemGroup>typeof(List<Category>),
  140. <ItemGroup>
  141.     <RdXmlFile Include="rd.xml" />
  142. </ItemGroup><ItemGroup>
  143.     <RdXmlFile Include="rd.xml" />
  144. </ItemGroup><ItemGroup>
  145.     <RdXmlFile Include="rd.xml" />
  146. </ItemGroup>typeof(Category),
  147. <ItemGroup>
  148.     <RdXmlFile Include="rd.xml" />
  149. </ItemGroup><ItemGroup>
  150.     <RdXmlFile Include="rd.xml" />
  151. </ItemGroup><ItemGroup>
  152.     <RdXmlFile Include="rd.xml" />
  153. </ItemGroup>typeof(WordCloud),
  154. <ItemGroup>
  155.     <RdXmlFile Include="rd.xml" />
  156. </ItemGroup><ItemGroup>
  157.     <RdXmlFile Include="rd.xml" />
  158. </ItemGroup><ItemGroup>
  159.     <RdXmlFile Include="rd.xml" />
  160. </ItemGroup>// 添加所有模型类型
  161. <ItemGroup>
  162.     <RdXmlFile Include="rd.xml" />
  163. </ItemGroup><ItemGroup>
  164.     <RdXmlFile Include="rd.xml" />
  165. </ItemGroup>};
  166. <ItemGroup>
  167.     <RdXmlFile Include="rd.xml" />
  168. </ItemGroup><ItemGroup>
  169.     <RdXmlFile Include="rd.xml" />
  170. </ItemGroup>// 触发类型加载
  171. <ItemGroup>
  172.     <RdXmlFile Include="rd.xml" />
  173. </ItemGroup><ItemGroup>
  174.     <RdXmlFile Include="rd.xml" />
  175. </ItemGroup>foreach (var type in types)
  176. <ItemGroup>
  177.     <RdXmlFile Include="rd.xml" />
  178. </ItemGroup><ItemGroup>
  179.     <RdXmlFile Include="rd.xml" />
  180. </ItemGroup>{
  181. <ItemGroup>
  182.     <RdXmlFile Include="rd.xml" />
  183. </ItemGroup><ItemGroup>
  184.     <RdXmlFile Include="rd.xml" />
  185. </ItemGroup><ItemGroup>
  186.     <RdXmlFile Include="rd.xml" />
  187. </ItemGroup>var _ = type.FullName;
  188. <ItemGroup>
  189.     <RdXmlFile Include="rd.xml" />
  190. </ItemGroup><ItemGroup>
  191.     <RdXmlFile Include="rd.xml" />
  192. </ItemGroup>}
  193. <ItemGroup>
  194.     <RdXmlFile Include="rd.xml" />
  195. </ItemGroup>}
  196. }
复制代码
修改ApiService类

修改ApiService类,确保在初始化时注册类型:
  1. using Refit;
  2. using StarBlogPublisher.Services.StarBlogApi;
  3. using System;
  4. using System.Net;
  5. using System.Net.Http;
  6. using Newtonsoft.Json;
  7. namespace StarBlogPublisher.Services;
  8. public class ApiService {
  9. <ItemGroup>
  10.     <RdXmlFile Include="rd.xml" />
  11. </ItemGroup>private static ApiService? _instance;
  12. <ItemGroup>
  13.     <RdXmlFile Include="rd.xml" />
  14. </ItemGroup>public static ApiService Instance {
  15. <ItemGroup>
  16.     <RdXmlFile Include="rd.xml" />
  17. </ItemGroup><ItemGroup>
  18.     <RdXmlFile Include="rd.xml" />
  19. </ItemGroup>get {
  20. <ItemGroup>
  21.     <RdXmlFile Include="rd.xml" />
  22. </ItemGroup><ItemGroup>
  23.     <RdXmlFile Include="rd.xml" />
  24. </ItemGroup><ItemGroup>
  25.     <RdXmlFile Include="rd.xml" />
  26. </ItemGroup>_instance ??= new ApiService();
  27. <ItemGroup>
  28.     <RdXmlFile Include="rd.xml" />
  29. </ItemGroup><ItemGroup>
  30.     <RdXmlFile Include="rd.xml" />
  31. </ItemGroup><ItemGroup>
  32.     <RdXmlFile Include="rd.xml" />
  33. </ItemGroup>return _instance;
  34. <ItemGroup>
  35.     <RdXmlFile Include="rd.xml" />
  36. </ItemGroup><ItemGroup>
  37.     <RdXmlFile Include="rd.xml" />
  38. </ItemGroup>}
  39. <ItemGroup>
  40.     <RdXmlFile Include="rd.xml" />
  41. </ItemGroup>}
  42. <ItemGroup>
  43.     <RdXmlFile Include="rd.xml" />
  44. </ItemGroup>private readonly RefitSettings _refitSettings;
  45. <ItemGroup>
  46.     <RdXmlFile Include="rd.xml" />
  47. </ItemGroup>private ApiService() {
  48. <ItemGroup>
  49.     <RdXmlFile Include="rd.xml" />
  50. </ItemGroup><ItemGroup>
  51.     <RdXmlFile Include="rd.xml" />
  52. </ItemGroup>// 确保类型被注册
  53. <ItemGroup>
  54.     <RdXmlFile Include="rd.xml" />
  55. </ItemGroup><ItemGroup>
  56.     <RdXmlFile Include="rd.xml" />
  57. </ItemGroup>RefitTypeRegistration.RegisterTypes();
  58. <ItemGroup>
  59.     <RdXmlFile Include="rd.xml" />
  60. </ItemGroup><ItemGroup>
  61.     <RdXmlFile Include="rd.xml" />
  62. </ItemGroup>
  63. <ItemGroup>
  64.     <RdXmlFile Include="rd.xml" />
  65. </ItemGroup><ItemGroup>
  66.     <RdXmlFile Include="rd.xml" />
  67. </ItemGroup>// 配置Refit设置
  68. <ItemGroup>
  69.     <RdXmlFile Include="rd.xml" />
  70. </ItemGroup><ItemGroup>
  71.     <RdXmlFile Include="rd.xml" />
  72. </ItemGroup>_refitSettings = new RefitSettings(new NewtonsoftJsonContentSerializer(
  73. <ItemGroup>
  74.     <RdXmlFile Include="rd.xml" />
  75. </ItemGroup><ItemGroup>
  76.     <RdXmlFile Include="rd.xml" />
  77. </ItemGroup><ItemGroup>
  78.     <RdXmlFile Include="rd.xml" />
  79. </ItemGroup>new JsonSerializerSettings
  80. <ItemGroup>
  81.     <RdXmlFile Include="rd.xml" />
  82. </ItemGroup><ItemGroup>
  83.     <RdXmlFile Include="rd.xml" />
  84. </ItemGroup><ItemGroup>
  85.     <RdXmlFile Include="rd.xml" />
  86. </ItemGroup>{
  87. <ItemGroup>
  88.     <RdXmlFile Include="rd.xml" />
  89. </ItemGroup><ItemGroup>
  90.     <RdXmlFile Include="rd.xml" />
  91. </ItemGroup><ItemGroup>
  92.     <RdXmlFile Include="rd.xml" />
  93. </ItemGroup><ItemGroup>
  94.     <RdXmlFile Include="rd.xml" />
  95. </ItemGroup>TypeNameHandling = TypeNameHandling.Auto,
  96. <ItemGroup>
  97.     <RdXmlFile Include="rd.xml" />
  98. </ItemGroup><ItemGroup>
  99.     <RdXmlFile Include="rd.xml" />
  100. </ItemGroup><ItemGroup>
  101.     <RdXmlFile Include="rd.xml" />
  102. </ItemGroup><ItemGroup>
  103.     <RdXmlFile Include="rd.xml" />
  104. </ItemGroup>// 禁用反射优化,这在AOT环境中很重要
  105. <ItemGroup>
  106.     <RdXmlFile Include="rd.xml" />
  107. </ItemGroup><ItemGroup>
  108.     <RdXmlFile Include="rd.xml" />
  109. </ItemGroup><ItemGroup>
  110.     <RdXmlFile Include="rd.xml" />
  111. </ItemGroup><ItemGroup>
  112.     <RdXmlFile Include="rd.xml" />
  113. </ItemGroup>TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple
  114. <ItemGroup>
  115.     <RdXmlFile Include="rd.xml" />
  116. </ItemGroup><ItemGroup>
  117.     <RdXmlFile Include="rd.xml" />
  118. </ItemGroup><ItemGroup>
  119.     <RdXmlFile Include="rd.xml" />
  120. </ItemGroup>}
  121. <ItemGroup>
  122.     <RdXmlFile Include="rd.xml" />
  123. </ItemGroup><ItemGroup>
  124.     <RdXmlFile Include="rd.xml" />
  125. </ItemGroup>));
  126. <ItemGroup>
  127.     <RdXmlFile Include="rd.xml" />
  128. </ItemGroup>}
  129. <ItemGroup>
  130.     <RdXmlFile Include="rd.xml" />
  131. </ItemGroup>// ... 其余代码保持不变 ...
  132. }
复制代码
在App.axaml.cs中初始化类型注册

确保在应用启动时调用类型注册:
  1. public override void OnFrameworkInitializationCompleted()
  2. {
  3. <ItemGroup>
  4.     <RdXmlFile Include="rd.xml" />
  5. </ItemGroup>// 确保Refit类型被注册
  6. <ItemGroup>
  7.     <RdXmlFile Include="rd.xml" />
  8. </ItemGroup>RefitTypeRegistration.RegisterTypes();
  9. <ItemGroup>
  10.     <RdXmlFile Include="rd.xml" />
  11. </ItemGroup>
  12. <ItemGroup>
  13.     <RdXmlFile Include="rd.xml" />
  14. </ItemGroup>// 其他初始化代码...
  15. <ItemGroup>
  16.     <RdXmlFile Include="rd.xml" />
  17. </ItemGroup>
  18. <ItemGroup>
  19.     <RdXmlFile Include="rd.xml" />
  20. </ItemGroup>base.OnFrameworkInitializationCompleted();
  21. }
复制代码
添加AOT兼容性配置

由于AOT编译对反射和动态代码生成有限制,需要添加一个rd.xml文件来指定需要保留的类型:
  1. <Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
  2. <ItemGroup>
  3.     <RdXmlFile Include="rd.xml" />
  4. </ItemGroup>
  5. <ItemGroup>
  6.     <RdXmlFile Include="rd.xml" />
  7. </ItemGroup><ItemGroup>
  8.     <RdXmlFile Include="rd.xml" />
  9. </ItemGroup>
  10. <ItemGroup>
  11.     <RdXmlFile Include="rd.xml" />
  12. </ItemGroup><ItemGroup>
  13.     <RdXmlFile Include="rd.xml" />
  14. </ItemGroup>
  15. <ItemGroup>
  16.     <RdXmlFile Include="rd.xml" />
  17. </ItemGroup><ItemGroup>
  18.     <RdXmlFile Include="rd.xml" />
  19. </ItemGroup>
  20. <ItemGroup>
  21.     <RdXmlFile Include="rd.xml" />
  22. </ItemGroup><ItemGroup>
  23.     <RdXmlFile Include="rd.xml" />
  24. </ItemGroup>
  25. <ItemGroup>
  26.     <RdXmlFile Include="rd.xml" />
  27. </ItemGroup><ItemGroup>
  28.     <RdXmlFile Include="rd.xml" />
  29. </ItemGroup>
  30. <ItemGroup>
  31.     <RdXmlFile Include="rd.xml" />
  32. </ItemGroup><ItemGroup>
  33.     <RdXmlFile Include="rd.xml" />
  34. </ItemGroup>
  35. <ItemGroup>
  36.     <RdXmlFile Include="rd.xml" />
  37. </ItemGroup><ItemGroup>
  38.     <RdXmlFile Include="rd.xml" />
  39. </ItemGroup>
  40. <ItemGroup>
  41.     <RdXmlFile Include="rd.xml" />
  42. </ItemGroup><ItemGroup>
  43.     <RdXmlFile Include="rd.xml" />
  44. </ItemGroup>
  45. <ItemGroup>
  46.     <RdXmlFile Include="rd.xml" />
  47. </ItemGroup><ItemGroup>
  48.     <RdXmlFile Include="rd.xml" />
  49. </ItemGroup>
  50. <ItemGroup>
  51.     <RdXmlFile Include="rd.xml" />
  52. </ItemGroup><ItemGroup>
  53.     <RdXmlFile Include="rd.xml" />
  54. </ItemGroup><ItemGroup>
  55.     <RdXmlFile Include="rd.xml" />
  56. </ItemGroup><Type Name="StarBlogPublisher.Services.StarBlogApi.IAuth" Dynamic="Required All" />
  57. <ItemGroup>
  58.     <RdXmlFile Include="rd.xml" />
  59. </ItemGroup><ItemGroup>
  60.     <RdXmlFile Include="rd.xml" />
  61. </ItemGroup><ItemGroup>
  62.     <RdXmlFile Include="rd.xml" />
  63. </ItemGroup><Type Name="StarBlogPublisher.Services.StarBlogApi.ICategory" Dynamic="Required All" />
  64. <ItemGroup>
  65.     <RdXmlFile Include="rd.xml" />
  66. </ItemGroup><ItemGroup>
  67.     <RdXmlFile Include="rd.xml" />
  68. </ItemGroup><ItemGroup>
  69.     <RdXmlFile Include="rd.xml" />
  70. </ItemGroup><Type Name="StarBlogPublisher.Services.StarBlogApi.IBlogPost" Dynamic="Required All" />
  71. <ItemGroup>
  72.     <RdXmlFile Include="rd.xml" />
  73. </ItemGroup><ItemGroup>
  74.     <RdXmlFile Include="rd.xml" />
  75. </ItemGroup></Assembly>
  76. <ItemGroup>
  77.     <RdXmlFile Include="rd.xml" />
  78. </ItemGroup><ItemGroup>
  79.     <RdXmlFile Include="rd.xml" />
  80. </ItemGroup>
  81. <ItemGroup>
  82.     <RdXmlFile Include="rd.xml" />
  83. </ItemGroup><ItemGroup>
  84.     <RdXmlFile Include="rd.xml" />
  85. </ItemGroup>
  86. <ItemGroup>
  87.     <RdXmlFile Include="rd.xml" />
  88. </ItemGroup><ItemGroup>
  89.     <RdXmlFile Include="rd.xml" />
  90. </ItemGroup><ItemGroup>
  91.     <RdXmlFile Include="rd.xml" />
  92. </ItemGroup><Type Name="CodeLab.Share.ViewModels.Response.ApiResponse`1" Dynamic="Required All" />
  93. <ItemGroup>
  94.     <RdXmlFile Include="rd.xml" />
  95. </ItemGroup><ItemGroup>
  96.     <RdXmlFile Include="rd.xml" />
  97. </ItemGroup></Assembly>
  98. <ItemGroup>
  99.     <RdXmlFile Include="rd.xml" />
  100. </ItemGroup></Application>
  101. </Directives>
复制代码
然后在项目文件中引用这个rd.xml文件:
  1. <ItemGroup>
  2.     <RdXmlFile Include="rd.xml" />
  3. </ItemGroup>
复制代码
发布

使用以下命令发布AOT版本的应用程序:
  1. dotnet publish -c Release -r win-x64 -p:PublishAot=true
复制代码
对于其他平台,可以替换相应的RID:

  • Windows: win-x64
  • macOS: osx-x64
  • Linux: linux-x64
小结

AOT 发布是 .Net 平台一个重要的特性,它能将应用程序编译成不依赖运行时的机器码,不仅减小了发布包体积,还能提升启动速度,同时也增加了反编译的难度。
使用 AOT 方式发布 Avalonia 应用程序还是有一些坑的。:JSON序列化问题、类型注册问题以及AOT兼容性问题。针对这些问题,以下解决方案可以解决:

  • JSON序列化方面,优先选择了对AOT支持更好的 Newtonsoft.Json 库,并通过类型预注册确保了序列化的正确性。
  • 对于需要反射的功能,通过rd.xml文件显式声明需要保留的类型,解决了AOT编译时的类型裁剪问题。
  • 在项目配置方面,通过合理设置AOT相关的编译选项,平衡了性能和包大小。
虽然AOT发布还存在一些限制,比如调试相对困难、部分第三方库可能不兼容等,但随着.Net平台的发展(特别是.Net9之后的版本),AOT的支持会越来越完善。对于需要高性能、小体积、反编译保护的Avalonia应用来说,AOT发布是一个值得考虑的选择。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册