Migración del proyecto de enlace de Xamarin.Android
En .NET, no hay ningún concepto de un proyecto de enlace de Android como un tipo de proyecto independiente. Cualquiera de los grupos de elementos de MSBuild o acciones de compilación que funcionan en proyectos de enlace de Xamarin.Android se admiten a través de una biblioteca o aplicación .NET para Android.
Para migrar una biblioteca de enlaces de Xamarin.Android a una biblioteca de clases de .NET para Android:
En Visual Studio, crea un proyecto de enlace de biblioteca de Java para Android con el mismo nombre que el proyecto de enlace de Xamarin.Android:
Al abrir el archivo del proyecto, se confirmará que tienes un proyecto de estilo SDK de .NET:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net8.0-android</TargetFramework> <SupportedOSPlatformVersion>21</SupportedOSPlatformVersion> <Nullable>enable</Nullable> <ImplicitUsings>enable</ImplicitUsings> </PropertyGroup> </Project>
Nota:
El archivo del proyecto de una biblioteca de enlace de Android es idéntico al archivo del proyecto de una biblioteca de clases de Android.
Agrega el archivo de Java (JAR) o de Android (AAR) al proyecto y asegúrate de que su acción de compilación esté establecida en AndroidLibrary.
Copia las transformaciones o adiciones de la biblioteca de enlaces de Xamarin.Android.
Opciones heredadas no admitidas
Ya no se admiten las siguientes opciones heredadas. Las alternativas admitidas están disponibles durante varios años y la opción de migración más sencilla es actualizar y probar los proyectos actuales con estas opciones antes de migrarlos a .NET.
AndroidClassParser
jar2xml
ya no es una opción válida para la propiedad $(AndroidClassParser)
. class-parse
es ahora la única opción admitida, así como la predeterminada.
class-parse
aprovecha muchas características modernas nuevas que no están disponibles en jar2xml
, como:
- Nombres de parámetros automáticos para los métodos de clase (si el código Java se compila con
javac -parameters
). - Compatibilidad con Kotlin.
- Compatibilidad con miembros de interfaz estáticos/predeterminados (DIM).
- Compatibilidad con anotaciones en los tipos de referencia que aceptan valores NULL (NRT) de Java.
AndroidCodegenTarget
XamarinAndroid
ya no es una opción válida para la propiedad $(AndroidCodegenTarget)
. XAJavaInterop1
es ahora la única opción admitida, así como la predeterminada.
Si tienes código enlazado manualmente en los archivos Additions
que interactúan con el código de enlace generado, es posible que deba actualizarse para que sea compatible con XAJavaInterop1
.
Inclusión de archivos predeterminada
Dada la siguiente estructura de archivos:
Transforms/
Metadata.xml
foo.jar
Los archivos Transforms\*.xml
se incluyen automáticamente como un elemento @(TransformFile)
y los archivos .jar
/.aar
se incluyen automáticamente como un elemento @(AndroidLibrary)
. Esto enlazará los tipos de C# para los tipos de Java que se encuentran en foo.jar
mediante las correcciones de metadatos de Transforms\Metadata.xml
.
El comportamiento predeterminado de comodines de archivos relacionado con Android se define en AutoImport.props. Este comportamiento se puede deshabilitar para los elementos de Android estableciendo la propiedad $(EnableDefaultAndroidItems)
en false
, o se puede deshabilitar todo el comportamiento predeterminado de inclusión de elementos estableciendo la propiedad $(EnableDefaultItems)
en false
.
Los archivos .jar
o .aar
no deseados se pueden incluir con los caracteres comodín predeterminados. Por ejemplo, los siguientes errores del compilador de C# se derivan de un archivo AndroidStudio\gradle\wrapper\gradle-wrapper.jar
enlazado involuntariamente:
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)'
Para solucionar este problema, puedes quitar el archivo específico en el archivo del proyecto:
<ItemGroup>
<AndroidLibrary Remove="AndroidStudio\gradle\wrapper\gradle-wrapper.jar" />
</ItemGroup>
Como alternativa, puedes excluir todos los archivos de una carpeta:
<AndroidLibrary Remove="AndroidStudio\**\*" />
Nuevos nombres de grupos de elementos
<AndroidLibrary>
es ahora el grupo de elementos recomendado que se usará para todos los archivos .jar
y .aar
. En Xamarin.Android, se usaron los siguientes grupos de elementos, que en su lugar pueden usar metadatos de elementos para lograr el mismo resultado:
Grupo de elementos heredado | Nuevo grupo de elementos | Metadatos de elementos | Tipo de proyecto heredado |
---|---|---|---|
AndroidAarLibrary |
AndroidLibrary |
Bind="false" |
Application |
AndroidJavaLibrary |
AndroidLibrary |
Bind="false" |
Biblioteca de aplicaciones o clases |
EmbeddedJar |
AndroidLibrary |
N/D | Proyecto de enlace |
EmbeddedReferenceJar |
AndroidLibrary |
Bind="false" |
Proyecto de enlace |
InputJar |
AndroidLibrary |
Pack="false" |
Proyecto de enlace |
LibraryProjectZip |
AndroidLibrary |
N/D | Proyecto de enlace |
Considere un archivo .aar
o .jar
en el que no está interesado en incluir un enlace de C#. Esto es habitual en los casos en los que tiene dependencias de Java o Kotlin a las que no es necesario llamar desde C#. En este caso, puedes establecer los metadatos Bind
en false
. De forma predeterminada, los caracteres comodín predeterminados recogen el archivo. También puedes usar el atributo Update
para establecer los metadatos Bind
:
<ItemGroup>
<AndroidLibrary Update="foo.jar" Bind="false">
</ItemGroup>
En un proyecto de biblioteca de clases de Android, esto redistribuiría el archivo .jar
dentro del paquete NuGet resultante tal cual. En un proyecto de aplicación de Android, esto incluiría el archivo .jar
en el archivo .aab
o .apk
resultante. Tampoco incluiría un enlace de C# para esta biblioteca de Java.
Archivos JAR/AAR incrustados
En Xamarin.Android, los archivos .jar
o .aar
de Java a menudo se insertaban en el enlace .dll
como un recurso incrustado. Pero esto llevó a compilaciones lentas, ya que cada .dll
debe abrirse y examinarse en busca de código Java. Si se encuentra, se debe extraer en el disco que se va a usar.
En .NET, el código Java ya no está insertado en .dll
. El proceso de compilación de la aplicación incluirá automáticamente los archivos .jar
o .aar
que encuentre en el mismo directorio que un .dll
al que hace referencia.
Si un proyecto hace referencia a un enlace a través de <PackageReference>
o <ProjectReference>
, todo funciona y no se requieren consideraciones adicionales. Pero si un proyecto hace referencia a un enlace a través de <Reference>
, el archivo .aar
.jar
/ debe estar situado junto al archivo .dll
. Es decir, para la siguiente referencia:
<Reference Include="MyBinding.dll" />
Un directorio como el del ejemplo siguiente no funcionará:
lib/
MyBinding.dll
En su lugar, el directorio también debe contener el código nativo:
lib/
MyBinding.dll
mybinding.jar
Consideraciones de migración
Hay varias características nuevas establecidas de forma predeterminada para ayudar a generar enlaces que se ajusten mejor a sus homólogos de Java. Pero si vas a migrar un proyecto de enlace existente, estas características pueden crear enlaces que no sean compatibles con la API de los enlaces existentes. Para mantener la compatibilidad, es posible que quieras deshabilitar o modificar estas nuevas características.
Constantes de interfaz
Tradicionalmente, C# no ha permitido que las constantes se declaren en una interface
, lo cual es un patrón común en Java:
public interface Foo {
public static int BAR = 1;
}
Este patrón se admitía anteriormente mediante la creación de una class
alternativa que contiene las constantes:
public abstract class Foo : Java.Lang.Object
{
public static int Bar = 1;
}
Con C# 8, estas constantes se colocan en la interface
:
public interface IFoo
{
public static int Bar = 1;
}
Pero esto significa que la clase alternativa de la que puede depender el código existente ya no se genera.
Al establecer la propiedad $(AndroidBoundInterfacesContainConstants)
en false
en el archivo del proyecto, se revertirá al comportamiento heredado.
Tipos de interfaz anidados
Tradicionalmente, C# no ha permitido que los tipos anidados se declaren en una interface
, lo cual está permitido en Java:
public interface Foo {
public class Bar { }
}
Este patrón se admitía moviendo el tipo anidado a un tipo de nivel superior con un nombre generado compuesto por la interfaz y el nombre del tipo anidado:
public interface IFoo { }
public class IFooBar : Java.Lang.Object { }
Con C# 8, los tipos anidados se pueden colocar en la interface
:
public interface IFoo
{
public class Bar : Java.Lang.Object { }
}
Pero esto significa que la clase de nivel superior de la que puede depender el código existente ya no se genera.
Al establecer la propiedad $(AndroidBoundInterfacesContainTypes)
en false
en el archivo del proyecto, se revertirá al comportamiento heredado.
Si quieres usar un enfoque híbrido, por ejemplo, para mantener los tipos anidados existentes que se han movido a un tipo de nivel superior, pero permitir que los tipos anidados futuros permanezcan anidados, puedes hacerlo en el nivel interface
especificando metadata
para establecer el atributo unnest
. Si se establece en true
, se producirá un "desanidamiento" de los tipos anidados (comportamiento heredado):
<attr path="/api/package[@name='my.package']/interface[@name='Foo']" name="unnest">true</attr>
Si se establece en false
, los tipos anidados permanecerán anidados en la interface
(comportamiento de .NET):
<attr path="/api/package[@name='my.package']/interface[@name='Foo']" name="unnest">false</attr>
Con este enfoque, podrías dejar la propiedad $(AndroidBoundInterfacesContainTypes)
como true
y establecer unnest
en true
para cada interface
con los tipos anidados que tienes actualmente. Estos siempre seguirán siendo tipos de nivel superior, mientras que los nuevos tipos anidados introducidos más adelante se anidarán.
Miembros de interfaz estáticos y predeterminados (DIM)
Tradicionalmente, C# no ha permitido que las interfaces contengan miembros static
y métodos default
:
public interface Foo {
public static void Bar () { ... }
public default void Baz () { ... }
}
Se han admitido miembros estáticos en interfaces moviéndolos a una class
del mismo nivel:
public interface IFoo { }
public class Foo
{
public static void Bar () { ... }
}
Los métodos de interfaces default
no se han enlazado tradicionalmente, ya que no son necesarios y no se ha producido una construcción de C# para admitirlos.
Con C# 8 static
y default
los miembros se admiten en interfaces, lo que refleja la interfaz de Java:
public interface IFoo
{
public static void Bar () { ... }
public default void Baz () { ... }
}
Pero esto significa que la class
alternativa del mismo nivel que contiene miembros static
ya no se generará.
Al establecer la propiedad $AndroidBoundInterfacesContainStaticAndDefaultInterfaceMethods
en false
en el archivo del proyecto, se revertirá al comportamiento heredado.
Tipos de referencia que aceptan valores NULL
Se ha agregado compatibilidad con tipos de referencia que aceptan valores NULL (NRT) en Xamarin.Android 11.0. La compatibilidad con NRT se puede habilitar mediante el mecanismo de .NET estándar:
<PropertyGroup>
<Nullable>enable</Nullable>
</PropertyGroup>
Dado que el valor predeterminado para .NET es disable
, el mismo se aplica a los proyectos de Xamarin.Android.
Resource.designer.cs
En Xamarin.Android, los proyectos de enlace de Java no admiten la generación de un archivo Resource.designer.cs
. Dado que los proyectos de enlace son simplemente bibliotecas de clases en .NET, este archivo se generará. Esto podría ser un cambio importante al migrar proyectos existentes.
Un ejemplo de error de este cambio es si tu enlace genera una clase denominada Resource
en el espacio de nombres raíz:
error CS0101: The namespace 'MyBinding' already contains a definition for 'Resource'
O bien, en el caso de AndroidX, hay archivos de proyecto con -
en el nombre, como androidx.window/window-extensions.csproj
. Esto da como resultado el espacio de nombres raíz window-extensions
y C# no válido en 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
Para deshabilitar la generación Resource.designer.cs
, establece la propiedad $(AndroidGenerateResourceDesigner)
en false
en el archivo del proyecto:
<PropertyGroup>
<AndroidGenerateResourceDesigner>false</AndroidGenerateResourceDesigner>
</PropertyGroup>