Condividi tramite


Migrazione del progetto di associazione Xamarin.Android

In .NET non esiste alcun concetto di progetto di associazione Android come tipo di progetto separato. Qualsiasi gruppo di elementi MSBuild o azioni di compilazione che funzionano nei progetti di associazione Xamarin.Android è supportato tramite un'app o una libreria .NET per Android.

Per eseguire la migrazione di una libreria di binding Xamarin.Android a una libreria di classi .NET per Android:

  1. In Visual Studio creare un nuovo progetto di associazione di librerie Java Android con lo stesso nome del progetto di associazione Xamarin.Android:

    Screenshot della creazione di un progetto di associazione di libreria Java Android in Visual Studio.

    L'apertura del file di progetto consentirà di verificare di avere un progetto di tipo .NET SDK:

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

    Nota

    Il file di progetto per una libreria di associazioni Android è identico al file di progetto per una libreria di classi Android.

  2. Aggiungere l'archivio Java (JAR) o Android Archive (AAR) al progetto e assicurarsi che l'azione di compilazione sia impostata su AndroidLibrary.

  3. Copiare eventuali trasformazioni o aggiunte dalla libreria di associazioni di Xamarin.Android.

Opzioni legacy non supportate

Le opzioni legacy seguenti non sono più supportate. Le alternative supportate sono disponibili per diversi anni e l'opzione di migrazione più fluida consiste nell'aggiornare e testare i progetti correnti con queste opzioni prima di eseguirne la migrazione a .NET.

AndroidClassParser

jar2xml non è più un'opzione valida per la $(AndroidClassParser) proprietà . class-parse è ora l'opzione predefinita e supportata solo.

class-parse sfrutta molte nuove funzionalità moderne non disponibili in jar2xml, ad esempio:

  • Nomi di parametri automatici per i metodi di classe (se il codice Java viene compilato con javac -parameters).
  • Supporto di Kotlin.
  • Supporto del membro di interfaccia statico/predefinito ( DIM).
  • Supporto delle annotazioni NRT (Java Nullable Reference Type).

AndroidCodegenTarget

XamarinAndroid non è più un'opzione valida per la $(AndroidCodegenTarget) proprietà . XAJavaInterop1 è ora l'opzione predefinita e supportata solo.

Se nei file è presente Additions codice associato a mano che interagisce con il codice di associazione generato, potrebbe essere necessario aggiornarlo per essere compatibile con XAJavaInterop1.

Inclusione file predefinita

Data la struttura di file seguente:

Transforms/
    Metadata.xml
foo.jar

Transforms\*.xmli file vengono inclusi automaticamente come @(TransformFile) elemento e.aar .jar/i file vengono inclusi automaticamente come @(AndroidLibrary) elemento. In questo modo verranno associati tipi C# per i tipi Java trovati in foo.jar usando le correzioni dei metadati da Transforms\Metadata.xml.

Il comportamento predefinito del globbing del file correlato ad Android è definito in AutoImport.props. Questo comportamento può essere disabilitato per gli elementi Android impostando la $(EnableDefaultAndroidItems) proprietà su falseoppure è possibile disabilitare il comportamento di inclusione di tutti gli elementi predefiniti impostando la $(EnableDefaultItems) proprietà su false.

I file o .aar non indesiderati .jar possono essere inclusi con i caratteri jolly predefiniti. Ad esempio, gli errori del compilatore C# seguenti generano un'associazione involontaria di un AndroidStudio\gradle\wrapper\gradle-wrapper.jar file:

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

Per risolvere questo problema, è possibile rimuovere il file specifico nel file di progetto:

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

In alternativa, è possibile escludere tutti i file all'interno di una cartella:

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

Nomi di nuovi gruppi di elementi

<AndroidLibrary> è ora il gruppo di elementi consigliato da usare per tutti i .jar file e .aar . In Xamarin.Android sono stati usati i gruppi di elementi seguenti, che possono invece usare i metadati degli elementi per ottenere lo stesso risultato:

Gruppo di elementi legacy Nuovo gruppo di elementi Metadati degli elementi Tipo di progetto legacy
AndroidAarLibrary AndroidLibrary Bind="false" Applicazione
AndroidJavaLibrary AndroidLibrary Bind="false" Libreria di applicazioni o classi
EmbeddedJar AndroidLibrary n/d Progetto di associazione
EmbeddedReferenceJar AndroidLibrary Bind="false" Progetto di associazione
InputJar AndroidLibrary Pack="false" Progetto di associazione
LibraryProjectZip AndroidLibrary n/d Progetto di associazione

Si consideri un .aar file o .jar , in cui non si è interessati a includere un'associazione C#. Questo è comune per i casi in cui si hanno dipendenze Java o Kotlin che non è necessario chiamare da C#. In questo caso, è possibile impostare i Bind metadati su false. Per impostazione predefinita, il file viene prelevato dai caratteri jolly predefiniti. È anche possibile usare l'attributo Update per impostare i Bind metadati:

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

In un progetto di libreria di classi Android, questo ridistribuirà il .jar file all'interno del pacchetto NuGet risultante così come è. In un progetto di applicazione Android, questo includerà il .jar file nel file o .aab risultante.apk. Nessuno dei due include un'associazione C# per questa libreria Java.

File JAR/AAR incorporati

In Xamarin.Android, Java .jar o .aar è stato spesso incorporato nell'associazione .dll come risorsa incorporata. Tuttavia, questo ha portato a compilazioni lente, perché ogni .dll compilazione deve essere aperta e analizzata per il codice Java. Se trovato, deve essere estratto su disco da usare.

In .NET il codice Java non è più incorporato in .dll. Il processo di compilazione dell'app include automaticamente tutti i .jar file o .aar trovati nella stessa directory di un riferimento .dll.

Se un progetto fa riferimento a un'associazione tramite <PackageReference> o <ProjectReference> quindi tutto funziona e non sono necessarie considerazioni aggiuntive. Tuttavia, se un progetto fa riferimento a un'associazione tramite <Reference>, deve.aar .jar/trovarsi accanto a ..dll Vale a dire, per il riferimento seguente:

<Reference Include="MyBinding.dll" />

Una directory come quella nell'esempio seguente non funzionerà:

lib/
    MyBinding.dll

La directory deve invece contenere anche il codice nativo:

lib/
    MyBinding.dll
    mybinding.jar

Considerazioni sulla migrazione

Esistono diverse nuove funzionalità impostate per impostazione predefinita per produrre associazioni che corrispondano meglio alle controparti Java. Tuttavia, se si esegue la migrazione di un progetto di associazione esistente, queste funzionalità possono creare associazioni non compatibili con le API con le associazioni esistenti. Per mantenere la compatibilità, è possibile disabilitare o modificare queste nuove funzionalità.

Costanti di interfaccia

In genere, C# non ha consentito la dichiarazione di costanti in un interfaceoggetto , che è un modello comune in Java:

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

Questo modello è stato precedentemente supportato creando un'alternativa class che contiene le costanti:

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

Con C# 8, queste costanti vengono posizionate in interface:

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

Ciò significa tuttavia che la classe alternativa da cui dipende il codice esistente non viene più generata.

Se si imposta la $(AndroidBoundInterfacesContainConstants) proprietà su false nel file di progetto, verrà ripristinato il comportamento legacy.

Tipi di interfaccia annidati

In genere, C# non ha consentito la dichiarazione di tipi annidati in un interfaceoggetto , che è consentito in Java:

public interface Foo {
     public class Bar { }
}

Questo modello è stato supportato spostando il tipo annidato in un tipo di primo livello con un nome generato composto dall'interfaccia e dal nome del tipo annidato:

public interface IFoo { }

public class IFooBar : Java.Lang.Object { }

Con C# 8, i tipi annidati possono essere inseriti in interface:

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

Ciò significa tuttavia che la classe di primo livello da cui il codice esistente può dipendere non viene più generato.

Se si imposta la $(AndroidBoundInterfacesContainTypes) proprietà su false nel file di progetto, verrà ripristinato il comportamento legacy.

Se si vuole usare un approccio ibrido, ad esempio, per mantenere i tipi annidati esistenti spostati in un tipo di primo livello, ma consentire a qualsiasi tipo annidato futuro di rimanere annidati, è possibile specificarlo a interface livello usando metadata per impostare l'attributo unnest . L'impostazione di su true comporterà l'annullamento dell'annidamento di qualsiasi tipo annidato (comportamento legacy):

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

Impostandolo su false , i tipi annidati rimangono annidati nel interface comportamento (.NET):

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

Usando questo approccio, è possibile lasciare la $(AndroidBoundInterfacesContainTypes) proprietà come true e impostare su true unnest per ogni interface con tipi annidati attualmente disponibili. Questi rimarranno sempre tipi di primo livello, mentre tutti i nuovi tipi annidati introdotti in un secondo momento verranno annidati.

Membri di interfaccia statici e predefiniti (DIM)

In genere, C# non ha consentito alle interfacce di contenere static membri e default metodi:

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

I membri statici nelle interfacce sono stati supportati spostandoli in un elemento di pari livello class:

public interface IFoo { }

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

default I metodi di interfaccia non sono tradizionalmente associati, poiché non sono necessari e non c'era un costrutto C# per supportarli.

Con C# 8 static e default i membri sono supportati nelle interfacce, eseguendo il mirroring dell'interfaccia Java:

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

Ciò significa tuttavia che l'elemento di pari livello class alternativo contenente static i membri non verrà più generato.

Se si imposta la $AndroidBoundInterfacesContainStaticAndDefaultInterfaceMethods proprietà su false nel file di progetto, verrà ripristinato il comportamento legacy.

Tipi riferimento nullable

Il supporto per i tipi di riferimento nullable (NRT) è stato aggiunto in Xamarin.Android 11.0. Il supporto NRT può essere abilitato usando il meccanismo .NET standard:

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

Poiché l'impostazione predefinita per .NET è disable, lo stesso vale per i progetti Xamarin.Android.

Resource.designer.cs

In Xamarin.Android i progetti di associazione Java non supportano la generazione di un Resource.designer.cs file. Poiché i progetti di associazione sono solo librerie di classi in .NET, questo file verrà generato. Potrebbe trattarsi di una modifica che causa un'interruzione durante la migrazione di progetti esistenti.

Un esempio di errore di questa modifica è se l'associazione genera una classe denominata Resource nello spazio dei nomi radice:

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

In alternativa, nel caso di AndroidX, sono presenti file di progetto con - nel nome, ad androidx.window/window-extensions.csprojesempio . Ciò comporta lo spazio dei nomi window-extensions radice e C# non valido 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

Per disabilitare Resource.designer.cs la generazione, impostare la $(AndroidGenerateResourceDesigner) proprietà su false nel file di progetto:

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