Поделиться через


Миграция проекта привязки Xamarin.Android

В .NET отсутствует концепция проекта привязки Android в качестве отдельного типа проекта. Любые группы элементов MSBuild или действия сборки, которые работают в проектах привязки Xamarin.Android, поддерживаются с помощью приложения или библиотеки .NET для Android.

Чтобы перенести библиотеку привязок Xamarin.Android в библиотеку классов .NET для Android:

  1. В Visual Studio создайте проект привязки библиотеки Java для Android с тем же именем, что и проект привязки Xamarin.Android:

    Снимок экрана: создание проекта привязки библиотеки Java для Android в Visual Studio.

    Открытие файла проекта подтвердит наличие проекта в стиле пакета SDK для .NET:

    <Project Sdk="Microsoft.NET.Sdk">
      <PropertyGroup>
        <TargetFramework>net8.0-android</TargetFramework>
        <SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
        <Nullable>enable</Nullable>
        <ImplicitUsings>enable</ImplicitUsings>
      </PropertyGroup>
    </Project>
    

    Замечание

    Файл проекта библиотеки привязки Android идентичен файлу проекта для библиотеки классов Android.

  2. Добавьте в проект архив Java (JAR) или архив Android (AAR) и убедитесь, что его действие сборки установлено на AndroidLibrary.

  3. Скопируйте любые преобразования или дополнения из библиотеки привязок Xamarin.Android.

Неподдерживаемые устаревшие параметры

Следующие устаревшие параметры больше не поддерживаются. Поддерживаемые альтернативные варианты доступны в течение нескольких лет, и самый плавный вариант миграции — обновить и протестировать текущие проекты с этими параметрами, прежде чем переносить их в .NET.

AndroidClassParser

jar2xml Больше не является допустимым параметром $(AndroidClassParser) для свойства. class-parse теперь является параметром по умолчанию и единственным поддерживаемым параметром.

class-parse использует преимущества многих новых современных функций, недоступных в jar2xml, например:

  • Автоматические имена параметров для методов класса (если код Java компилируется с javac -parameters).
  • Поддержка Kotlin.
  • Поддержка статических и стандартных элементов интерфейса (DIM).
  • Поддержка аннотаций нулевых ссылочных типов (NRT) в Java.

AndroidCodegenTarget

XamarinAndroid Больше не является допустимым параметром $(AndroidCodegenTarget) для свойства. XAJavaInterop1 теперь является параметром по умолчанию и единственным поддерживаемым вариантом.

Если у вас есть ручной код в Additions файлах, взаимодействующих с созданным кодом привязки, его может потребоваться обновить, чтобы быть совместимым с XAJavaInterop1.

Включение файлов по умолчанию

Учитывая следующую структуру файлов:

Transforms/
    Metadata.xml
foo.jar

Transforms\*.xmlфайлы автоматически включаются в качестве @(TransformFile) элемента, а.aar.jar/файлы автоматически включаются в качестве @(AndroidLibrary) элемента. Это связывает типы C# с типами Java, найденными в foo.jar, используя исправления метаданных из Transforms\Metadata.xml.

Поведение глоббинга файлов Android по умолчанию определяется в AutoImport.props. Это поведение можно отключить для элементов Android, присвоив $(EnableDefaultAndroidItems) свойству falseзначение , или все поведение включения элементов по умолчанию можно отключить, задав $(EnableDefaultItems) для свойства значение false.

Нежелательные .jar или .aar файлы могут быть включены при использовании подстановочных знаков по умолчанию. Например, из-за непреднамеренно привязанного файла AndroidStudio\gradle\wrapper\gradle-wrapper.jar возникают следующие ошибки компилятора C#.

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 n/a Проект привязки
EmbeddedReferenceJar AndroidLibrary Bind="false" Проект привязки
InputJar AndroidLibrary Pack="false" Проект привязки
LibraryProjectZip AndroidLibrary n/a Проект привязки

Рассмотрим файл .aar или .jar, в который вы не хотите включать привязку C#. Это часто происходит в тех случаях, когда у вас есть зависимости Java или Kotlin, которые не нужно вызывать из C#. В этом случае можно задать метаданные Bind значением false. По умолчанию файл выбирается подстановочными знаками по умолчанию. Вы также можете использовать атрибут Update для задания метаданных Bind.

<ItemGroup>
  <AndroidLibrary Update="foo.jar" Bind="false">
</ItemGroup>

В проекте библиотеки классов Android этот файл будет распространяться .jar внутри результирующего пакета NuGet в исходном виде. В проекте приложения Android это будет включать .jar файл в результирующий .apk или .aab файл. Ни та, ни другая не будут включать привязку C# для этой библиотеки Java.

Внедренные JAR/AAR-файлы

В Xamarin.Android Java .jar или .aar часто был внедрен в привязку .dll как внедренный ресурс. Однако это привело к медленным сборкам, так как каждый .dll должен быть открыт и сканирован для кода Java. При обнаружении его необходимо распаковать на диск для использования.

В .NET код Java больше не внедряется в .dll. Процесс сборки приложения автоматически включает любые файлы .jar или .aar, которые он находит в том же каталоге вместе с .dll.

Если проект ссылается на привязку через <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;
}

Однако это означает, что альтернативный класс, от который существующий код может зависеть, больше не создается.

Установка свойства 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 и установить unnest в true для каждого interface, который имеет вложенные типы в настоящее время. Они всегда будут оставаться типами верхнего уровня, а все новые вложенные типы, представленные позже, будут вложены.

Статические и стандартные элементы интерфейса (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 () { ... }
}

Однако это означает, что альтернативный элемент-сиблинг class, содержащий static члены, больше не будет создан.

Установка свойства false в файле проекта $AndroidBoundInterfacesContainStaticAndDefaultInterfaceMethods позволит вернуться к поведению, характерному для старых версий.

Ссылочные типы, допускающие значение null

Добавлена поддержка ссылочных типов, допускающих значение NULL (NRT), в Xamarin.Android 11.0. Поддержка NRT может быть включена с помощью стандартного механизма .NET:

<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 имен и некорректного C# кода в Resource.designer.cs.

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, задайте значение false для свойства $(AndroidGenerateResourceDesigner) в файле проекта.

<PropertyGroup>
  <AndroidGenerateResourceDesigner>false</AndroidGenerateResourceDesigner>
</PropertyGroup>