Partager via


Migration d’un projet de liaison Xamarin.Android

Dans .NET, il n’existe aucun concept de projet de liaison Android en tant que type de projet distinct. Les groupes d’éléments MSBuild ou les actions de génération qui fonctionnent dans des projets de liaison Xamarin.Android sont pris en charge via une application ou une bibliothèque .NET pour Android.

Pour migrer une bibliothèque de liaisons Xamarin.Android vers une bibliothèque de classes .NET pour Android :

  1. Dans Visual Studio, créez un projet de liaison de bibliothèques Java Android portant le même nom que votre projet de liaison Xamarin.Android :

    Capture d’écran de la création d’un projet de liaison de bibliothèques Java Android dans Visual Studio.

    L’ouverture du fichier projet confirmera que vous disposez d’un projet de style SDK .NET :

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

    Remarque

    Le fichier projet d’une bibliothèque de liaisons Android est identique au fichier projet d’une bibliothèque de classes Android.

  2. Ajoutez votre archive Java (JAR) ou Android (AAR) au projet et vérifiez que son action de génération est définie sur AndroidLibrary.

  3. Copiez les transformations ou ajouts à partir de votre bibliothèque de liaisons Xamarin.Android.

Options héritées non prises en charge

Les options héritées suivantes ne sont plus prises en charge. Des alternatives prises en charge sont disponibles depuis plusieurs années, et l’option de migration la plus fluide consiste à mettre à jour et tester vos projets actuels avec ces options avant de les migrer vers .NET.

AndroidClassParser

jar2xml n’est plus une option valide de la propriété $(AndroidClassParser). class-parse est désormais l’option par défaut et la seule option prise en charge.

class-parse bénéficie des nombreuses nouvelles fonctionnalités modernes qui n’étaient pas disponibles dans jar2xml, telles que :

  • Noms des paramètres automatiques des méthodes de classe (si votre code Java est compilé avec javac -parameters).
  • Prise en charge Kotlin.
  • Prise en charge du membre d’interface statique/par défaut (DIM).
  • Prise en charge des annotations de type référence pouvant accepter la valeur Null (NRT) de Java.

AndroidCodegenTarget

XamarinAndroid n’est plus une option valide de la propriété $(AndroidCodegenTarget). XAJavaInterop1 est désormais l’option par défaut et la seule option prise en charge.

Si vos fichiers Additions contiennent du code écrit manuellement qui interagit avec le code de liaison généré, il peut être nécessaire de le mettre à jour pour qu’il soit compatible avec XAJavaInterop1.

Inclusion de fichier par défaut

Étant donné la structure de fichier suivante :

Transforms/
    Metadata.xml
foo.jar

Des fichiers Transforms\*.xml sont automatiquement inclus comme élément @(TransformFile) et des fichiers .jar/.aar sont automatiquement inclus comme élément @(AndroidLibrary). Cela lie les types C# des types Java trouvés dans foo.jar à l’aide des correctifs de métadonnées de Transforms\Metadata.xml.

Le comportement de globbing de fichier associé à un Android par défaut est défini dans AutoImport.props. Ce comportement peut être désactivé sur des éléments Android en définissant la propriété $(EnableDefaultAndroidItems) sur false, ou les comportements d’inclusion d’élément par défaut peuvent être désactivés en définissant la propriété $(EnableDefaultItems) sur false.

Les fichiers .jar ou .aar non souhaités peuvent être inclus avec les caractères génériques par défaut. Par exemple, les erreurs du compilateur C# suivantes proviennent d’un fichier AndroidStudio\gradle\wrapper\gradle-wrapper.jar lié involontairement :

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)'

Pour résoudre ce problème, vous pouvez supprimer le fichier spécifique dans votre fichier projet :

<ItemGroup>
  <AndroidLibrary Remove="AndroidStudio\gradle\wrapper\gradle-wrapper.jar" />
</ItemGroup>

Vous pouvez également exclure tous les fichiers d’un dossier :

<AndroidLibrary Remove="AndroidStudio\**\*" />

Nouveaux noms de groupes d’éléments

<AndroidLibrary> est désormais le groupe d’éléments qu’il est recommandé d’utiliser pour les fichiers .jar et .aar. Dans Xamarin.Android, les groupes d’éléments suivants, qui peuvent utiliser à la place des métadonnées d’élément pour obtenir le même résultat, ont été utilisés :

Groupe d’éléments hérité Nouveau groupe d’éléments Métadonnées d’élément Type de projet hérité
AndroidAarLibrary AndroidLibrary Bind="false" Application
AndroidJavaLibrary AndroidLibrary Bind="false" Bibliothèque de classes ou d’applications
EmbeddedJar AndroidLibrary n/a Projet de liaison
EmbeddedReferenceJar AndroidLibrary Bind="false" Projet de liaison
InputJar AndroidLibrary Pack="false" Projet de liaison
LibraryProjectZip AndroidLibrary n/a Projet de liaison

Prenez un fichier .aar ou .jar que vous ne souhaitez pas inclure dans une liaison C#. Cela est courant si vous avez des dépendances Java ou Kotlin que vous n’avez pas besoin d’appeler à partir de C#. Dans ce cas, vous pouvez définir les métadonnées Bind sur false. Par défaut, le fichier est récupéré selon les caractères génériques par défaut. Vous pouvez également utiliser l’attribut Update pour définir les métadonnées Bind :

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

Dans un projet de bibliothèque de classes Android, cela redistribue tel quel le fichier .jar à l’intérieur du package NuGet résultant. Dans un projet d’application Android, cela inclut le fichier .jar dans le fichier .apk ou .aab résultant. Aucun des deux n’inclurait de liaison C# pour cette bibliothèque Java.

Fichiers JAR/AAR incorporés

Dans Xamarin.Android, les fichiers .jar ou .aar Java ont souvent été incorporés dans la liaison .dll comme ressource incorporée. Toutefois, cela a ralenti les générations, car chaque .dll doit être ouvert et son code Java analysé. S’il est trouvé, il doit être extrait sur le disque pour être utilisé.

Dans .NET, le code Java n’est plus incorporé dans le .dll. Le processus de génération de l’application inclut automatiquement les fichiers .jar ou .aar qu’il trouve dans le même répertoire que le .dll référencé.

Si un projet fait référence à une liaison via <PackageReference> ou <ProjectReference>, tout fonctionne et aucune autre considération n’est requise. Toutefois, si un projet fait référence à une liaison via <Reference>, le .jar/.aar doit être situé à côté du .dll. Autrement dit, pour la référence suivante :

<Reference Include="MyBinding.dll" />

Un répertoire tel que celui de l’exemple suivant ne fonctionnera pas :

lib/
    MyBinding.dll

En revanche, le répertoire doit également contenir le code natif :

lib/
    MyBinding.dll
    mybinding.jar

Considérations relatives à la migration

Il existe plusieurs nouvelles fonctionnalités définies par défaut pour aider à produire des liaisons qui correspondent mieux à leurs équivalents Java. Toutefois, si vous migrez un projet de liaison existant, ces fonctionnalités peuvent créer des liaisons qui ne sont pas compatibles avec les API de vos liaisons existantes. Pour maintenir la compatibilité, vous souhaiterez peut-être désactiver ou modifier ces nouvelles fonctionnalités.

Constantes d’interface

Traditionnellement, C# ne permet pas de déclarer de constantes dans une interface, ce qui est un modèle courant en Java :

public interface Foo {
     public static int BAR = 1;
}

Ce modèle était précédemment pris en charge en créant une autre class qui contient les constantes :

public abstract class Foo : Java.Lang.Object
{
   public static int Bar = 1;
}

Avec C# 8, ces constantes sont placées sur l’interface :

public interface IFoo
{
    public static int Bar = 1;
}

Toutefois, cela signifie que l’autre classe, dont peut dépendre le code existant, n’est plus générée.

La définition de la propriété $(AndroidBoundInterfacesContainConstants) sur false dans votre fichier projet rétablit le comportement hérité.

Types d'interface imbriqués

Traditionnellement, C# ne permet pas de déclarer des types imbriqués dans une interface, ce qui est autorisé en Java :

public interface Foo {
     public class Bar { }
}

Ce modèle était pris en charge en déplaçant le type imbriqué vers un type de niveau supérieur avec un nom généré composé de l’interface et du nom du type imbriqué :

public interface IFoo { }

public class IFooBar : Java.Lang.Object { }

Avec C# 8, les types imbriqués peuvent être placés dans l’interface :

public interface IFoo
{
    public class Bar : Java.Lang.Object { }
}

Toutefois, cela signifie que la classe de niveau supérieur, dont peut dépendre le code existant, n’est plus générée.

La définition de la propriété $(AndroidBoundInterfacesContainTypes) sur false dans votre fichier projet rétablit le comportement hérité.

Si vous souhaitez utiliser une approche hybride, par exemple pour conserver les types imbriqués existants déplacés vers un type de niveau supérieur, mais autoriser les futurs types imbriqués à rester imbriqués, vous pouvez le spécifier au niveau de l’interface à l’aide de metadata pour définir l’attribut unnest. La définition de ce paramètre sur true entraîne « l’annulation de l’imbrication » de tous les types imbriqués (comportement hérité) :

<attr path="/api/package[@name='my.package']/interface[@name='Foo']" name="unnest">true</attr>

La définition sur la valeur false se traduit par des types imbriqués restants imbriqués dans l’interface (comportement .NET) :

<attr path="/api/package[@name='my.package']/interface[@name='Foo']" name="unnest">false</attr>

Avec cette approche, vous pouvez laisser la propriété $(AndroidBoundInterfacesContainTypes) comme true et définir unnest sur true pour chaque interface ayant les types imbriqués actuels. Ces types restent toujours de niveau supérieur, tandis que les nouveaux types imbriqués créés ultérieurement seront imbriqués.

Membres d’interface statiques et par défaut (DIM)

Traditionnellement, C# n’autorise pas les interfaces à contenir des membres static et des méthodes default :

public interface Foo {
  public static void Bar () { ... }
  public default void Baz () { ... }
}

Les membres statiques sur les interfaces étaient pris en charge en les déplaçant vers une class sœur :

public interface IFoo { }

public class Foo
{
    public static void Bar () { ... }
}

Les méthodes d’interface default n’étaient traditionnellement pas liées, car elles n’étaient pas requises et il n’y avait pas de construction C# pour les prendre en charge.

Avec C# 8, les membres static et default sont pris en charge sur les interfaces, à l’instar de l’interface Java :

public interface IFoo
{
    public static void Bar () { ... }
    public default void Baz () { ... }
}

Toutefois, cela signifie que l’autre class sœur contenant les membres static ne sera plus générée.

La définition de la propriété $AndroidBoundInterfacesContainStaticAndDefaultInterfaceMethods sur false dans votre fichier projet rétablit le comportement hérité.

Types références Nullables

La prise en charge des types de référence pouvant accepter la valeur Null (NRT) a été ajoutée dans Xamarin.Android 11.0. La prise en charge des NRT peut être activée à l’aide du mécanisme .NET standard :

<PropertyGroup>
  <Nullable>enable</Nullable>
</PropertyGroup>

La valeur par défaut de .NET étant disable, il en est de même pour les projets Xamarin.Android.

Resource.designer.cs

Dans Xamarin.Android, les projets de liaison Java ne prenaient pas en charge la génération d’un fichier Resource.designer.cs. Les projets de liaison étant uniquement des bibliothèques de classes dans .NET, ce fichier sera généré. Il peut s’agir d’un changement cassant lors de la migration de projets existants.

L’un des exemples d’échec de cette modification est la génération par votre liaison d’une classe nommée Resource dans l’espace de noms racine :

error CS0101: The namespace 'MyBinding' already contains a definition for 'Resource'

Ou dans le cas d’AndroidX, l’existence de fichiers projet dont le nom contient un - tel que androidx.window/window-extensions.csproj. Cela génère un espace de noms racine window-extensions et un fichier Resource.designer.cs non valide dans 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

Pour désactiver la génération du fichier Resource.designer.cs, définissez la propriété $(AndroidGenerateResourceDesigner) sur false dans votre fichier projet :

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