Freigeben über


Migration von Xamarin.Android-Bindungsprojekten

In .NET gibt es kein Konzept für ein Android-Bindungsprojekt als separaten Projekttyp. Alle MSBuild-Elementgruppen oder Build-Aktionen, die in Xamarin.Android-Bindungsprojekten funktionieren, werden durch eine .NET for Android-App oder -Bibliothek unterstützt.

So migrieren Sie eine Xamarin.Android-Bindungsbibliothek zu einer .NET for Android-Klassenbibliothek:

  1. Erstellen Sie in Visual Studio ein neues Android Java Bibliotheks-Bindungsprojekt mit demselben Namen wie Ihr Xamarin.Android Bindungsprojekt:

    Screenshot des Erstellens eines Projekts für eine Android Java Bibliotheksbindung in Visual Studio.

    Wenn Sie die Projektdatei öffnen, wird bestätigt, dass es sich um ein .NET-Projekt im SDK-Format handelt:

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

    Hinweis

    Die Projektdatei für eine Android-Bindungsbibliothek ist identisch mit der Projektdatei für eine Android-Klassenbibliothek.

  2. Fügen Sie Ihr Java-Archiv (JAR) oder Android-Archiv (AAR) zum Projekt hinzu und stellen Sie sicher, dass die Build-Aktion auf AndroidLibrary festgelegt ist.

  3. Kopieren Sie alle Transformationen oder Ergänzungen aus Ihrer Xamarin.Android-Bindungsbibliothek.

Nicht unterstützte veraltete Optionen

Die folgenden veralteten Optionen werden nicht mehr unterstützt. Die unterstützten Optionen sind bereits seit mehreren Jahren verfügbar. Die einfachste Migrationsmöglichkeit besteht darin, Ihre aktuellen Projekte mit diesen Optionen zu aktualisieren und zu testen, bevor Sie sie zu .NET migrieren.

AndroidClassParser

jar2xml ist keine gültige Option mehr für die Eigenschaft $(AndroidClassParser). class-parse ist nun die Standardeinstellung und die einzige unterstützte Option.

class-parse verwendet viele neue und moderne Funktionen, die in jar2xml nicht verfügbar sind, wie z. B.:

  • Automatische Parameternamen für Klassenmethoden (wenn Ihr Java-Code mit javac -parameters kompiliert wurde).
  • Kotlin-Support.
  • Support für statische/standardmäßige Schnittstellenmember (DIM).
  • Support für Anmerkungen zu Nullable-Verweistypen (NRT) in Java.

AndroidCodegenTarget

XamarinAndroid ist keine gültige Option mehr für die Eigenschaft $(AndroidCodegenTarget). XAJavaInterop1 ist nun die Standardeinstellung und die einzige unterstützte Option.

Wenn Ihre Additions-Dateien manuell gebundenen Code enthalten, der mit dem generierten Bindungscode interagiert, muss dieser aktualisiert werden, um XAJavaInterop1kompatibel zu sein.

Standardmäßige Dateieinbindung

Mit der folgenden Dateistruktur:

Transforms/
    Metadata.xml
foo.jar

Transforms\*.xml-Dateien sind automatisch als @(TransformFile)-Element eingeschlossen, und .jar/.aar-Dateien sind automatisch als @(AndroidLibrary)-Element eingeschlossen. Dies bindet C#-Typen für die in foo.jar gefundenen Java-Typen unter Verwendung der Metadatenkorrekturen von Transforms\Metadata.xml.

Das Standard-Globbingverhalten von Android-Dateien ist in AutoImport.props definiert. Sie können dieses Verhalten für Android-Elemente deaktivieren, indem Sie die Eigenschaft $(EnableDefaultAndroidItems) auf false setzen. Sie können auch das gesamte Standardverhalten der Elemente deaktivieren, indem Sie die Eigenschaft $(EnableDefaultItems) auf false setzen.

Unerwünschte .jar- oder .aar-Dateien können mit den Standard-Platzhaltern eingeschlossen werden. Der folgende C#-Compilerfehler resultiert z. B. aus dem unbeabsichtigten Binden einer AndroidStudio\gradle\wrapper\gradle-wrapper.jar-Datei:

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

Um dieses Problem zu beheben, können Sie die betreffende Datei aus Ihrer Projektdatei entfernen:

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

Alternativ können Sie alle Dateien in einem Ordner ausschließen:

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

Neue Artikelgruppennamen

<AndroidLibrary> ist jetzt die empfohlene Artikelgruppe für alle .jar- und .aar-Dateien. In Xamarin.Android wurden die folgenden Artikelgruppen verwendet, die stattdessen Artikelmetadaten verwenden können, um dasselbe Ergebnis zu erzielen:

Legacy-Artikelgruppe Neue Artikelgruppe Artikelmetadaten Legacy-Projekttyp
AndroidAarLibrary AndroidLibrary Bind="false" Anwendung
AndroidJavaLibrary AndroidLibrary Bind="false" Anwendungs- oder Klassenbibliothek
EmbeddedJar AndroidLibrary Nicht zutreffend Bindungsprojekt
EmbeddedReferenceJar AndroidLibrary Bind="false" Bindungsprojekt
InputJar AndroidLibrary Pack="false" Bindungsprojekt
LibraryProjectZip AndroidLibrary Nicht zutreffend Bindungsprojekt

Ziehen Sie eine .aar- oder eine .jar-Datei in Betracht, an der Sie nicht interessiert sind, eine C#-Bindung einzugeben. Dies ist üblich für Fälle, in denen Sie über Java- oder Kotlin-Abhängigkeiten verfügen, die Sie nicht von C# aufrufen müssen. In diesem Fall können Sie die Bind-Metadaten auf false festlegen. Standardmäßig wird die Datei von den Standardplatzhaltern ausgewählt. Sie können auch das Update-Attribut verwenden, um die Bind-Metadaten festzulegen:

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

In einem Android-Klassenbibliotheksprojekt würde dies die .jar-Datei wie folgt innerhalb des resultierenden NuGet-Pakets neu verteilen. In einem Android-Anwendungsprojekt würde dies die .jar-Datei in die resultierende .apk- oder .aab-Datei einschließen. Keine würde eine C#-Bindung für diese Java-Bibliothek enthalten.

Eingebettete JAR/AAR-Dateien

In Xamarin.Android wurde Java .jar oder .aar häufig als Ressource in .dll eingebettet. Dies hatte jedoch langsame Builds zur Folge, da jedes .dll geöffnet und nach Java-Code durchsucht werden musste. Wurde Java-Code gefunden, musste er zur Verwendung auf die Festplatte extrahiert werden.

In .NET ist der Java-Code nicht mehr in die .dll eingebettet. Der App-Buildprozess schließt automatisch alle .jar- oder .aar-Dateien ein, die sich im selben Verzeichnis wie eine referenzierte .dll-Datei befinden.

Wenn ein Projekt eine Bindung über <PackageReference> oder <ProjectReference> verweist, funktioniert alles und es sind keine zusätzlichen Überlegungen erforderlich. Wenn ein Projekt jedoch eine Bindung über <Reference> verweist, muss sich die .jar/.aar neben der .dll befinden. Das bedeutet für den folgenden Verweis:

<Reference Include="MyBinding.dll" />

Ein Verzeichnis wie im folgenden Beispiel wird nicht funktionieren:

lib/
    MyBinding.dll

Vielmehr muss das Verzeichnis auch den systemeigenen Code enthalten:

lib/
    MyBinding.dll
    mybinding.jar

Migrationsüberlegungen

Es gibt mehrere neue Funktionen, die standardmäßig eingestellt sind, um Bindungen zu erzeugen, die besser zu ihren Java-Entsprechungen passen. Wenn Sie jedoch ein bestehendes Bindungsprojekt migrieren, können diese Funktionen Bindungen erzeugen, die nicht API-kompatibel zu Ihren bestehenden Bindungen sind. Um die Kompatibilität zu gewährleisten, sollten Sie diese neuen Funktionen deaktivieren oder ändern.

Schnittstellenkonstanten

Traditionell ist es in C# nicht erlaubt, Konstanten in einem interface zu deklarieren, was in Java ein gängiges Muster ist:

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

Dieses Muster wurde bisher durch die Erstellung einer Alternative class unterstützt, die die Konstanten enthält:

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

Mit C# 8 werden diese Konstanten auf interface gesetzt:

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

Dies hat jedoch zur Folge, dass die alternative Klasse, von der der vorhandene Code abhängig sein könnte, nicht mehr generiert wird.

Wenn Sie die Eigenschaft $(AndroidBoundInterfacesContainConstants) in Ihrer Projektdatei auf false setzen, wird das Legacyverhalten wiederhergestellt.

Geschachtelte Schnittstellentypen

Traditionell ist es in C# nicht erlaubt, geschachtelte Typen in einem interface zu deklarieren, während dies in Java erlaubt ist:

public interface Foo {
     public class Bar { }
}

Dieses Muster wurde unterstützt, indem der geschachtelte Typ in einen Typ der obersten Ebene mit einem generierten Namen verschoben wurde, der sich aus dem Namen der Schnittstelle und des geschachtelten Typs zusammensetzt:

public interface IFoo { }

public class IFooBar : Java.Lang.Object { }

Mit C# 8 können geschachtelte Typen in interface platziert werden:

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

Dies hat jedoch zur Folge, dass die oberste Klasse, von der der vorhandene Code abhängig sein könnte, nicht mehr generiert wird.

Wenn Sie die Eigenschaft $(AndroidBoundInterfacesContainTypes) in Ihrer Projektdatei auf false setzen, wird das Legacyverhalten wiederhergestellt.

Wenn Sie einen hybriden Ansatz verwenden möchten, z. B. um bestehende geschachtelte Typen in einen Typ der obersten Ebene zu verschieben, aber weiterhin zukünftige geschachtelte Typen zuzulassen, können Sie dies auf Ebene interface festlegen, indem Sie metadata verwenden, um das Attribut unnest zu setzen. Die Einstellung true bewirkt, dass alle geschachtelten Typen „unverschachtelt“ werden (Legacyverhalten):

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

Die Einstellung false bewirkt, dass geschachtelte Typen in interface geschachtelt bleiben (.NET-Verhalten):

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

Mit diesem Ansatz können Sie die Eigenschaft $(AndroidBoundInterfacesContainTypes) auf true belassen und unnest auf true für jedes interface mit geschachtelten Typen setzen, das Sie derzeit haben. Diese bleiben immer Typen der obersten Ebene, während alle später eingeführten geschachtelten Typen geschachtelt werden.

Statische und standardmäßige Schnittstellenmember (DIM)

Normalerweise ist es in C# nicht erlaubt, dass Schnittstellen static-Member und default-Methoden enthalten:

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

Statische Member auf Schnittstellen wurden durch Verschieben in ein gleichgeordnetes Element unterstützt class:

public interface IFoo { }

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

default Schnittstellenmethoden wurden normalerweise nicht gebunden, da sie nicht benötigt wurden und es kein C#-Konstrukt gab, das sie unterstützte.

Mit C# 8 werden static- und default-Members auf Schnittstellen unterstützt, genau wie bei der Java-Schnittstelle:

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

Dies bedeutet jedoch, dass das alternative gleichgeordnete Element class mit static-Members nicht mehr generiert wird.

Wenn Sie die Eigenschaft $AndroidBoundInterfacesContainStaticAndDefaultInterfaceMethods in Ihrer Projektdatei auf false setzen, wird das Legacyverhalten wiederhergestellt.

Nullwertfähige Verweistypen

Der Support für Nullwertfähige Verweistypen (Nullable Reference Types, NRT) wurde in Xamarin.Android 11.0 hinzugefügt. Der Support für NRT kann über den Standard-.NET-Mechanismus aktiviert werden:

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

Da die Standardeinstellung für .NET disable ist, gilt dies auch für Xamarin.Android-Projekte.

Resource.designer.cs

In Xamarin.Android unterstützten Java-Bindungsprojekte das Generieren einer Resource.designer.cs-Datei nicht. Diese Datei wird generiert, da es sich bei Bindungsprojekten lediglich um Klassenbibliotheken in .NET handelt. Dies könnte bei der Migration bestehender Projekte eine wichtige Änderung darstellen.

Ein Beispiel für einen Fehler aufgrund dieser Änderung ist, wenn Ihre Bindung eine Klasse mit dem Namen Resource im Stammnamespace generiert:

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

Oder bei AndroidX existieren Projektdateien mit - im Namen wie androidx.window/window-extensions.csproj. Dies führt zu dem Stammnamespace window-extensions und ungültigem C# in 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

Um die Generierung von Resource.designer.cs zu deaktivieren, setzen Sie die Eigenschaft $(AndroidGenerateResourceDesigner) in Ihrer Projektdatei auf false:

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