链接 .NET MAUI Android 应用

生成应用时,.NET Multi-platform App UI (.NET MAUI) 可以使用称作 ILLink 的链接器减小应用的整体大小。 ILLink 通过分析编译器生成的中间代码减小大小。 它删除未使用的方法、属性、字段、事件、结构和类,以生成仅包含运行应用所需的代码和程序集依赖项的应用。

链接器行为

链接器使你能剪裁 .NET MAUI Android 应用。 启用剪裁后,链接器会保持程序集不变,并通过删除应用不使用的类型和成员来减小 SDK 程序集的大小。

可以为应用的每个生成配置来配置链接器行为。 默认情况下,对调试版本将禁用剪裁,而对发布版本将启用剪裁。

警告

为应用的调试配置启用链接器可能会妨碍调试体验,因为它可能会删除让你能够检查对象状态的属性访问器。

  1. 在“解决方案资源管理器”中,右键单击 .NET MAUI 应用项目,然后选择“属性”。 然后,导航到“Android”>“选项”选项卡,并确保为发布版本配置启用剪裁:

    Screenshot of the linker behavior for Android in Visual Studio.

保留代码

使用链接器时,它有时会删除可能以动态方式(甚至以间接方式)调用的代码。 可以通过使用 DynamicDependency 特性批注链接器的方式指示链接器保留成员。 此特性可用于表示对成员类型与子集或特定成员的依赖关系。

重要

BCL 中无法静态确定供应用使用的每个成员都将被删除。

DynamicDependency 特性可以应用于构造函数、字段和方法:

[DynamicDependency("Helper", "MyType", "MyAssembly")]
static void RunHelper()
{
    var helper = Assembly.Load("MyAssembly").GetType("MyType").GetMethod("Helper");
    helper.Invoke(null, null);
}

在此示例中,DynamicDependency 确保 Helper 方法得到保留。 如果没有特性,链接将会从 MyAssembly 删除 Helper 或完全删除 MyAssembly(如果未在其他位置引用)。

该特性通过 stringDynamicallyAccessedMembers 特性指定要保留的成员。 类型和程序集要么隐含在属性上下文中,要么在属性中显式指定(按照分别表示类型和程序集名称的 Typestring)。

类型和成员字符串使用 C# 文档注释 ID 字符串格式的变体,不带成员前缀。 成员字符串不应包含声明类型的名称,可以省略参数以保留指定名称的所有成员。 以下示例显示有效的用途:

[DynamicDependency("Method()")]
[DynamicDependency("Method(System,Boolean,System.String)")]
[DynamicDependency("MethodOnDifferentType()", typeof(ContainingType))]
[DynamicDependency("MemberName")]
[DynamicDependency("MemberOnUnreferencedAssembly", "ContainingType", "UnreferencedAssembly")]
[DynamicDependency("MemberName", "Namespace.ContainingType.NestedType", "Assembly")]
// generics
[DynamicDependency("GenericMethodName``1")]
[DynamicDependency("GenericMethod``2(``0,``1)")]
[DynamicDependency("MethodWithGenericParameterTypes(System.Collections.Generic.List{System.String})")]
[DynamicDependency("MethodOnGenericType(`0)", "GenericType`1", "UnreferencedAssembly")]
[DynamicDependency("MethodOnGenericType(`0)", typeof(GenericType<>))]

保留程序集

可以指定应从链接器进程中排除的程序集,同时允许正常链接其他程序集。 当无法轻松使用 DynamicDependency 特性或不控制链接的代码时,此方法非常有用。

链接所有程序集时,可以通过在项目文件中的 <ItemGroup> 标记中设置 TrimmerRootAssembly MSBuild 属性,告知链接器跳过程序集:

<ItemGroup>
  <TrimmerRootAssembly Include="MyAssembly" />
</ItemGroup>

注意

设置 TrimmerRootAssembly MSBuild 属性时不需要 .dll 扩展。

如果链接器跳过了某个程序集,则是将其视为“已取得 root 权限”,这意味着将保留该程序集及其所有静态理解的依赖项。 可以通过将更多 TrimmerRootAssembly MSBuild 属性添加到 <ItemGroup> 跳过其他程序集。

保留程序集、类型和成员

可以向链接器传递一个 XML 说明文件,该文件指定需要保留的程序集、类型和成员。

若要在链接所有程序集时从链接过程中排除成员,请将项目文件中 <ItemGroup> 标记中的 TrimmerRootDescriptor MSBuild 属性设置为定义要排除成员的 XML 文件:

<ItemGroup>
  <TrimmerRootDescriptor Include="MyRoots.xml" />
</ItemGroup>

然后,XML 文件使用剪裁器描述符格式定义要从链接中排除的成员:

<linker>
  <assembly fullname="MyAssembly">
    <type fullname="MyAssembly.MyClass">
      <method name="DynamicallyAccessedMethod" />
    </type>
  </assembly>
</linker>

在此示例中,XML 文件指定应用动态访问的方法,该方法已从链接中排除。

当 XML 中列出程序集、类型或成员时,默认操作是保留,这意味着无论链接器是否认为它被使用,它都会保留在输出中。

注意

保留标记所包含的内容并不确定。 如果不提供下一级的详细信息,它将包含所有子级。 如果未列出任何类型的程序集,则将保留所有程序集的类型和成员。

将程序集标记为链接器安全

如果项目中有一个库,或者你是可重用库的开发人员,并且希望链接器将程序集视为可链接,则可以通过将 IsTrimmable MSBuild 属性添加到程序集的项目文件的方式将程序集标记为链接器安全:

<PropertyGroup>
    <IsTrimmable>true</IsTrimmable>
</PropertyGroup>

这会将程序集标记为“可剪裁”,并为该项目启用剪裁警告。 “可剪裁”意味着程序集被视为与剪裁兼容,生成程序集时不应显示剪裁警告。 在经过剪裁的应用中使用时,会在最终输出中删除程序集未使用的成员。

IsTrimmable MSBuild 属性设置为项目文件中的 true 会将 AssemblyMetadata 特性插入程序集:

[assembly: AssemblyMetadata("IsTrimmable", "True")]

或者,可以将 AssemblyMetadata 特性添加到程序集中,而无需将 IsTrimmable MSBuild 属性添加到程序集的项目文件中。

注意

如果为程序集设置了 IsTrimmable MSBuild 属性,则会替代 AssemblyMetadata("IsTrimmable", "True") 特性。 这样可以选择对程序集进行剪裁(即使该程序集不包含该特性),或者禁用对具有该特性的程序集进行剪裁。

抑制分析警告

启用链接器后,将删除不可静态访问的 IL。 因此,使用反射或其他模式创建动态依赖项的应用可能会中断运行。 若要警告此类模式,在将程序集标记为链接器安全时,库作者应将 SuppressTrimAnalysisWarnings MSBuild 属性设置为 false

<PropertyGroup>
  <SuppressTrimAnalysisWarnings>false</SuppressTrimAnalysisWarnings>
</PropertyGroup>

不抑制剪裁分析警告将包括有关整个应用的警告,其中包括你自己的代码、库代码以及 SDK 代码。

显示详细警告

对于每个来自 PackageReference 的程序集,剪裁分析最多为其生成一个警告,以指示程序集的内部机制与剪裁功能不兼容。 作为库作者,将程序集标记为链接器安全时,应通过将 TrimmerSingleWarn MSBuild 属性设置成 false 为所有程序集启用单个警告:

<PropertyGroup>
  <TrimmerSingleWarn>false</TrimmerSingleWarn>
</PropertyGroup>

该设置显示所有详细警告,而不是将其折叠为每个程序集的单个警告。

另请参阅