Xamarin.Android 绑定项目迁移
在 .NET 中,没有将 Android 绑定项目作为单独项目类型的概念。 通过适用于 Android 应用或库的 .NET 支持在 Xamarin.Android 绑定项目中运行的任何 MSBuild 项组或生成操作。
将 Xamarin.Android 绑定库迁移到适用于 Android 的 .NET 类库:
在 Visual Studio 中,新建与 Xamarin.Android 绑定项目同名的 Android Java 库绑定项目:
打开项目文件将确认你有一个 .NET SDK 样式的项目:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net8.0-android</TargetFramework> <SupportedOSPlatformVersion>21</SupportedOSPlatformVersion> <Nullable>enable</Nullable> <ImplicitUsings>enable</ImplicitUsings> </PropertyGroup> </Project>
注意
Android 绑定库的项目文件与 Android 类库的项目文件相同。
将 Java 存档 (JAR) 或 Android 存档 (AAR) 添加到项目中,并确保其生成操作设置为 AndroidLibrary。
从 Xamarin.Android 绑定库复制任何转换或添加。
不支持的旧选项
不再支持以下旧选项。 受支持的替代方案已推出几年,最流畅的迁移选项是将当前项目迁移到 .NET 之前更新和测试当前项目。
AndroidClassParser
jar2xml
不再是属性 $(AndroidClassParser)
的有效选项。 class-parse
目前是默认且唯一受支持的选项。
class-parse
利用了 jar2xml
中没有的许多新式功能,例如:
- 类方法的自动参数名称(如果使用
javac -parameters
编译 Java 代码)。 - Kotlin 支持。
- 静态/默认接口成员 (DIM) 支持。
- Java 可为 Null 引用类型 (NRT) 批注支持。
AndroidCodegenTarget
XamarinAndroid
不再是属性 $(AndroidCodegenTarget)
的有效选项。 XAJavaInterop1
目前是默认且唯一受支持的选项。
如果 Additions
文件中有与生成的绑定代码交互的手动绑定代码,则可能需要将其更新以与 XAJavaInterop1
兼容。
默认文件包含
给定以下文件结构:
Transforms/
Metadata.xml
foo.jar
自动包含 Transforms\*.xml
文件作为 @(TransformFile)
项,且自动包含 .jar
/.aar
文件作为 @(AndroidLibrary)
项。 这将使用 Transforms\Metadata.xml
中的元数据修复来绑定 foo.jar
中找到的 Java 类型的 C# 类型。
AutoImport.props 中定义了默认的 Android 相关文件通配行为。 可以通过将 $(EnableDefaultAndroidItems)
属性设置为 false
来禁用 Android 项的此行为,也可以通过将 $(EnableDefaultItems)
属性设置为 false
来禁用所有默认项包含行为。
默认通配符可能会包含不需要的 .jar
或 .aar
文件。 例如,以下 C# 编译器错误源于无意中绑定 AndroidStudio\gradle\wrapper\gradle-wrapper.jar
文件:
Org.Gradle.Cli.AbstractCommandLineConverter.cs(11,89): error CS0535: 'Download' does not implement interface member 'IDownload.Download(URI, File)'
Org.Gradle.Wrapper.Download.cs(10,60): error CS0535: 'AbstractCommandLineConverter' does not implement interface member 'ICommandLineConverter.Convert(ParsedCommandLine, Object)'
要解决此问题,可以在项目文件中移除特定文件:
<ItemGroup>
<AndroidLibrary Remove="AndroidStudio\gradle\wrapper\gradle-wrapper.jar" />
</ItemGroup>
或者,可以排除文件夹中的所有文件:
<AndroidLibrary Remove="AndroidStudio\**\*" />
新项组名称
<AndroidLibrary>
目前建议用于所有 .jar
和 .aar
文件的推荐项组。 在 Xamarin.Android 中曾使用了以下项组,现在可以使用项元数据来实现相同的结果。
旧项组 | 新项组 | 项目元数据 | 旧项目类型 |
---|---|---|---|
AndroidAarLibrary |
AndroidLibrary |
Bind="false" |
应用程序 |
AndroidJavaLibrary |
AndroidLibrary |
Bind="false" |
应用或类库 |
EmbeddedJar |
AndroidLibrary |
不适用 | 绑定项目 |
EmbeddedReferenceJar |
AndroidLibrary |
Bind="false" |
绑定项目 |
InputJar |
AndroidLibrary |
Pack="false" |
绑定项目 |
LibraryProjectZip |
AndroidLibrary |
不适用 | 绑定项目 |
请考虑一个你对包括 C# 绑定不感兴趣的 .aar
或 .jar
文件。 如果你拥有不需要从 C# 调用的 Java 或 Kotlin 依赖项,那么这种情况很常见。 在这种情况下,可以将 Bind
元数据设置为 false
。 默认情况下,文件由默认通配符选取。 还可以使用 Update
特性设置 Bind
元数据:
<ItemGroup>
<AndroidLibrary Update="foo.jar" Bind="false">
</ItemGroup>
在 Android 类库项目中,这会按原样重新分发所生成 NuGet 包中的 .jar
文件。 在 Android 应用项目中,这将在生成的 .apk
或 .aab
文件中包括 .jar
文件。 这两个库都不会包含此 Java 库的 C# 绑定。
嵌入式 JAR/AAR 文件
在 Xamarin.Android 中,Java .jar
或 .aar
通常作为嵌入资源嵌入到绑定 .dll
中。 但是,这导致生成速度缓慢,因为必须打开并扫描每个 .dll
才能使用 Java 代码。 如果找到,则必须将其提取到要使用的磁盘。
在 .NET 中,Java 代码不再嵌入到 .dll
。 应用生成过程将自动包含在与引用的 .dll
位于同一目录中找到的任何 .jar
或 .aar
文件。
如果项目通过 <PackageReference>
或 <ProjectReference>
引用绑定,则一切正常,无需考虑其他注意事项。 但是,如果项目通过 <Reference>
引用绑定,则 .jar
/.aar
必须位于 .dll
的旁边。 也就是说,对于以下引用:
<Reference Include="MyBinding.dll" />
以下示例中的目录将不起作用:
lib/
MyBinding.dll
相反,目录还必须包含本机代码:
lib/
MyBinding.dll
mybinding.jar
迁移注意事项
默认情况下会设置多个新功能,以帮助生成更好地匹配其 Java 对应项的绑定。 但是,如果迁移的是已有的绑定项目,则这些功能有可能创建 API 与现有绑定不兼容的绑定。 为了保持兼容性,你可能希望禁用或修改这些新功能。
接口常量
传统上,C# 不允许在 interface
(即:Java 中的常见模式)中声明常量:
public interface Foo {
public static int BAR = 1;
}
以前支持这种模式的方法是创建包含常量的替代 class
:
public abstract class Foo : Java.Lang.Object
{
public static int Bar = 1;
}
使用 C# 8 时,这些常量放置于 interface
:
public interface IFoo
{
public static int Bar = 1;
}
但是,这意味着不再生成已有代码可能依赖的替代类。
将 $(AndroidBoundInterfacesContainConstants)
属性设置为项目文件中的 false
将会还原为旧行为。
嵌套接口类型
传统上,C# 不允许在 interface
中声明嵌套类型,但在 Java 中允许:
public interface Foo {
public class Bar { }
}
以前支持此模式的方法是:将嵌套类型移至带有由接口和嵌套类型名称组成的生成名称的顶级类型:
public interface IFoo { }
public class IFooBar : Java.Lang.Object { }
使用 C# 8,嵌套类型可以放置在 interface
:
public interface IFoo
{
public class Bar : Java.Lang.Object { }
}
但是,这意味着不再生成已有代码可能依赖的顶级类。
将 $(AndroidBoundInterfacesContainTypes)
属性设置为项目文件中的 false
将会还原为旧行为。
如果要使用混合方法(例如:为了将已有嵌套类型移动至顶级类型,但允许任何将来的嵌套类型保持嵌套状态),则可以在 interface
层通过使用 metadata
设置 unnest
特性对此进行指定。 将其设置为 true
将会导致“取消嵌套”任何嵌套类型(旧行为):
<attr path="/api/package[@name='my.package']/interface[@name='Foo']" name="unnest">true</attr>
将其设置为 false
将会导致嵌套类型仍嵌套在 interface
中(.NET 行为):
<attr path="/api/package[@name='my.package']/interface[@name='Foo']" name="unnest">false</attr>
使用此方法,可以将 $(AndroidBoundInterfacesContainTypes)
属性保留为 true
,以及使用你目前拥有的嵌套类型为每个 interface
将 unnest
设置为 true
。 这些将始终保持顶级类型,而后期引入的任何新嵌套类型都将被嵌套。
静态和默认接口成员 (DIM)
传统上,C# 不允许接口包含 static
成员和 default
方法:
public interface Foo {
public static void Bar () { ... }
public default void Baz () { ... }
}
通过将接口上的静态成员移至同级 class
对其提供支持:
public interface IFoo { }
public class Foo
{
public static void Bar () { ... }
}
default
接口方法传统上不绑定,因为它们非必需,也没有 C# 构造支持它们。
在 C# 8 中,接口上支持 static
和 default
成员,这与 Java 接口相同:
public interface IFoo
{
public static void Bar () { ... }
public default void Baz () { ... }
}
但是,这意味着不再生成包含 static
成员的替代同级 class
。
将 $AndroidBoundInterfacesContainStaticAndDefaultInterfaceMethods
属性设置为项目文件中的 false
将会还原为旧行为。
可为空引用类型
Xamarin.Android 11.0 中添加了对可为 Null 的引用类型 (NRT) 的支持。 可以使用标准 .NET 机制启用 NRT 支持:
<PropertyGroup>
<Nullable>enable</Nullable>
</PropertyGroup>
由于 .NET 的默认值为 disable
,因此它同样适用于 Xamarin.Android 项目。
Resource.designer.cs
在 Xamarin.Android 中,Java 绑定项目不支持生成 Resource.designer.cs
文件。 由于绑定项目只是 .NET 中的类库,因此将生成此文件。 这对迁移现有项目而言可能是一项重大更改。
此更改失败的一个示例是,如果绑定在根命名空间中生成名为 Resource
的类:
error CS0101: The namespace 'MyBinding' already contains a definition for 'Resource'
或者,在 AndroidX 中,有名称中带有 -
的项目文件,例如 androidx.window/window-extensions.csproj
。 这会导致根命名空间 window-extensions
和 Resource.designer.cs
中的 C# 无效:
error CS0116: A namespace cannot directly contain members such as fields, methods or statements
error CS1514: { expected
error CS1022: Type or namespace definition, or end-of-file expected
要禁用 Resource.designer.cs
生成,请在项目文件中将 $(AndroidGenerateResourceDesigner)
属性设置为 false
:
<PropertyGroup>
<AndroidGenerateResourceDesigner>false</AndroidGenerateResourceDesigner>
</PropertyGroup>