Dela via


Migrering av Xamarin.Android-bindningsprojekt

I .NET finns det inget koncept för ett Android-bindningsprojekt som en separat projekttyp. Alla MSBuild-objektgrupper eller byggåtgärder som fungerar i Xamarin.Android-bindningsprojekt stöds via en .NET för Android-app eller ett bibliotek.

Så här migrerar du ett Xamarin.Android-bindningsbibliotek till ett .NET för Android-klassbibliotek:

  1. I Visual Studio skapar du ett nytt Android Java-biblioteksbindningsprojekt med samma namn som ditt Xamarin.Android-bindningsprojekt:

    Skärmbild av hur du skapar ett Android Java-biblioteksbindningsprojekt i Visual Studio.

    Om du öppnar projektfilen bekräftas att du har ett .NET SDK-liknande projekt:

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

    Anmärkning

    Projektfilen för ett Android-bindningsbibliotek är identisk med projektfilen för ett Android-klassbibliotek.

  2. Lägg till Ditt Java-arkiv (JAR) eller Android Archive (AAR) i projektet och se till att dess byggåtgärd är inställd på AndroidLibrary.

  3. Kopiera eventuella transformeringar eller tillägg från ditt Xamarin.Android-bindningsbibliotek.

Äldre alternativ som inte stöds

Följande äldre alternativ stöds inte längre. Alternativen som stöds har varit tillgängliga i flera år och det smidigaste migreringsalternativet är att uppdatera och testa dina aktuella projekt med dessa alternativ innan du migrerar dem till .NET.

AndroidClassParser

jar2xml är inte längre ett giltigt alternativ för egenskapen $(AndroidClassParser) . class-parse är nu standardalternativet och stöds bara.

class-parse drar nytta av många nya moderna funktioner som inte är tillgängliga i jar2xml, till exempel:

  • Automatiska parameternamn för klassmetoder (om Java-koden kompileras med javac -parameters).
  • Kotlin-stöd.
  • Stöd för statiska/standardgränssnittselement (DIM).
  • Stöd för NRT-anteckningar (Java Nullable Reference Type).

AndroidCodegenTarget

XamarinAndroid är inte längre ett giltigt alternativ för egenskapen $(AndroidCodegenTarget) . XAJavaInterop1 är nu standardalternativet och stöds bara.

Om du har handbunden kod i dina Additions filer som interagerar med den genererade bindningskoden kan den behöva uppdateras för att vara kompatibel med XAJavaInterop1.

Standardfilinkludering

Med tanke på följande filstruktur:

Transforms/
    Metadata.xml
foo.jar

Transforms\*.xmlfiler inkluderas automatiskt som ett @(TransformFile) objekt och.aar.jar/filer inkluderas automatiskt som ett @(AndroidLibrary) objekt. Detta binder C#-typer till de Java-typer som finns i foo.jar med hjälp av metadatakorrigeringarna från Transforms\Metadata.xml.

Standardbeteendet för Android-relaterade filer definieras i AutoImport.props. Det här beteendet kan inaktiveras för Android-objekt genom att ställa in $(EnableDefaultAndroidItems) egenskapen på false, eller så kan alla standardbeteenden för objektinkludering inaktiveras genom att egenskapen anges $(EnableDefaultItems) till false.

Oönskade .jar filer eller .aar filer kan eventuellt inkluderas med standard wildcards. Följande C#-kompilatorfel beror till exempel på att en AndroidStudio\gradle\wrapper\gradle-wrapper.jar fil oavsiktligt är bunden:

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

Du kan lösa problemet genom att ta bort den specifika filen i projektfilen:

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

Du kan också exkludera alla filer i en mapp:

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

Namn på nya objektgrupper

<AndroidLibrary> är nu den rekommenderade objektgruppen som ska användas för alla .jar och .aar filer. I Xamarin.Android användes följande objektgrupper, som i stället kan använda objektmetadata för att uppnå samma resultat:

Äldre objektgrupp Ny objektgrupp Objektmetadata Äldre projekttyp
AndroidAarLibrary AndroidLibrary Bind="false" Ansökan
AndroidJavaLibrary AndroidLibrary Bind="false" Program- eller klassbibliotek
EmbeddedJar AndroidLibrary Inte tillämpligt Bindande projekt
EmbeddedReferenceJar AndroidLibrary Bind="false" Bindande projekt
InputJar AndroidLibrary Pack="false" Sammanbindningsprojekt
LibraryProjectZip AndroidLibrary Inte tillämpligt Bindningsprojekt

Överväg en .aar fil eller .jar en fil där du inte är intresserad av att inkludera en C#-bindning. Detta är vanligt i fall där du har Java- eller Kotlin-beroenden som du inte behöver anropa från C#. I det här fallet kan du ange Bind metadata som false. Som standard hämtas filen som standard jokertecken. Du kan också använda attributet Update för att ange Bind metadata:

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

I ett Android-klassbiblioteksprojekt omdistribuerar detta .jar-filen i det resulterande NuGet-paketet som den är. I ett Android-programprojekt skulle det inkludera .jar filen i den resulterande .apk filen eller .aab filen. Ingen av dem skulle innehålla en C#-bindning för det här Java-biblioteket.

Inbäddade JAR/AAR-filer

I Xamarin.Android inbäddades Java .jar eller .aar ofta i bindningen .dll som en inbäddad resurs. Detta ledde dock till långsamma versioner eftersom var och en .dll måste öppnas och genomsökas efter Java-kod. Om det hittas måste det extraheras till disken för att kunna användas.

I .NET är Java-koden inte längre inbäddad i .dll. Appbyggprocessen innehåller automatiskt alla .jar filer eller .aar filer som hittas i samma katalog som en refererad .dll.

Om ett projekt refererar till en bindning via <PackageReference> eller <ProjectReference> så fungerar allt och inga ytterligare överväganden krävs. Men, om ett projekt refererar till en bindning via <Reference>, måste .jar/.aar vara placerade bredvid .dll. Det vill säga, för följande referens:

<Reference Include="MyBinding.dll" />

En katalog som den i följande exempel fungerar inte:

lib/
    MyBinding.dll

I stället måste katalogen också innehålla den interna koden:

lib/
    MyBinding.dll
    mybinding.jar

Överväganden vid migrering

Det finns flera nya funktioner som har angetts som standard för att hjälpa till att skapa bindningar som bättre matchar deras Java-motsvarigheter. Men om du migrerar ett befintligt bindningsprojekt kan dessa funktioner skapa bindningar som inte är API-kompatibla med dina befintliga bindningar. För att upprätthålla kompatibiliteten kanske du vill inaktivera eller ändra dessa nya funktioner.

Gränssnittskonstanter

Traditionellt har C# inte tillåtit att konstanter deklareras i ett interface, vilket är ett vanligt mönster i Java:

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

Det här mönstret stöddes tidigare genom att skapa ett alternativ class som innehåller konstanterna:

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

Med C# 8 placeras dessa konstanter på interface:

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

Det innebär dock att den alternativa klass som befintlig kod kan vara beroende av inte längre genereras.

Om du anger egenskapen $(AndroidBoundInterfacesContainConstants) till false i projektfilen återgår du till det äldre beteendet.

Kapslade gränssnittstyper

Traditionellt har C# inte tillåtit att kapslade typer deklareras i en interface, som tillåts i Java:

public interface Foo {
     public class Bar { }
}

Det här mönstret stöds genom att flytta den kapslade typen till en toppnivåtyp med ett genererat namn som består av gränssnittet och det kapslade typnamnet:

public interface IFoo { }

public class IFooBar : Java.Lang.Object { }

Med C# 8 kan kapslade typer placeras i interface:

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

Det innebär dock att den högsta klassen som befintlig kod kan vara beroende av inte längre genereras.

Om du anger egenskapen $(AndroidBoundInterfacesContainTypes) till false i projektfilen återgår du till det äldre beteendet.

Om du vill använda en hybridmetod för att behålla befintliga kapslade typer flyttade till en toppnivåtyp, men tillåta att framtida kapslade typer förblir kapslade, kan du ange detta på interface-nivån genom att använda metadata för att ställa in unnest-attributet. Om du ställer in den på true resulterar det i att kapslade typer "av-nestas" (äldre beteende):

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

Om du ställer in den på false kommer kapslade typer att förbli kapslade i interface (.NET-beteendet):

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

Med den här metoden kan du lämna $(AndroidBoundInterfacesContainTypes) som true och ställa in unnesttrue för varje interface med kapslade typer som du för närvarande har. Dessa förblir alltid toppnivåtyper, medan alla nya kapslade typer som introduceras senare kapslas.

Statiska och standardgränssnittsmedlemmar (DIM)

Traditionellt har C# inte tillåtit gränssnitt att innehålla static medlemmar och default metoder:

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

Statiska medlemmar i gränssnitt har fått stöd genom att flytta dem till ett syskon class:

public interface IFoo { }

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

default gränssnittsmetoder har traditionellt inte varit bundna, eftersom de inte krävs och det inte fanns någon C#-konstruktion för att stödja dem.

Med C# 8 stöds static och default som medlemmar på gränssnitt, likt Java-gränssnittet:

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

Det innebär dock att det alternativa syskon class som innehåller static medlemmar inte längre genereras.

Om du anger egenskapen $AndroidBoundInterfacesContainStaticAndDefaultInterfaceMethods till false i projektfilen återgår du till det äldre beteendet.

Referenstyper som kan ogiltigförklaras

Stöd för nullbara referenstyper (NRT) har lagts till i Xamarin.Android 11.0. NRT-stöd kan aktiveras med hjälp av standardmetoden .NET:

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

Eftersom standardvärdet för .NET är disablegäller samma för Xamarin.Android-projekt.

Resource.designer.cs

I Xamarin.Android har Java-bindningsprojekt inte stöd för att generera en Resource.designer.cs fil. Eftersom bindningsprojekt bara är klassbibliotek i .NET genereras den här filen. Detta kan vara en icke-bakåtkompatibel ändring vid migrering av befintliga projekt.

Ett exempel på ett fel från den här ändringen är om bindningen genererar en klass med namnet Resource i rotnamnområdet:

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

Eller när det gäller AndroidX finns det projektfiler med - i namnet, till exempel androidx.window/window-extensions.csproj. Detta resulterar i rot-namnområdet window-extensions och ogiltig C# i 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

Om du vill inaktivera Resource.designer.cs generering anger du egenskapen $(AndroidGenerateResourceDesigner) till false i projektfilen:

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