Partilhar via


Migração de projeto de vinculação Xamarin.Android

No .NET, não há nenhum conceito de um projeto de vinculação Android como um tipo de projeto separado. Qualquer um dos grupos de itens do MSBuild ou ações de compilação que funcionam em projetos de vinculação do Xamarin.Android são suportados por meio de um aplicativo ou biblioteca .NET para Android.

Para migrar uma biblioteca de vinculação Xamarin.Android para uma biblioteca de classes .NET para Android:

  1. No Visual Studio, crie um novo projeto Android Java Library Binding com o mesmo nome do seu projeto de vinculação Xamarin.Android:

    Captura de tela da criação de um projeto Android Java Library Binding no Visual Studio.

    Abrir o arquivo de projeto confirmará que você tem um projeto no estilo SDK do .NET:

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

    Observação

    O arquivo de projeto para uma biblioteca de vinculação do Android é idêntico ao arquivo de projeto para uma biblioteca de classes do Android.

  2. Adicione seu Java Archive (JAR) ou Android Archive (AAR) ao projeto e certifique-se de que sua ação de compilação esteja definida como AndroidLibrary.

  3. Copie todas as transformações ou adições da sua biblioteca de ligações do Xamarin.Android.

Opções herdadas sem suporte

As seguintes opções herdadas não são mais suportadas. As alternativas suportadas estão disponíveis há vários anos, e a opção de migração mais suave é atualizar e testar seus projetos atuais com essas opções antes de migrá-los para o .NET.

AndroidClassParser

jar2xml já não é uma opção válida para a propriedade $(AndroidClassParser). class-parse é agora a opção padrão e apenas suportada.

class-parse tira partido de muitas novas funcionalidades modernas não disponíveis no jar2xml, tais como:

  • Nomes de parâmetros automáticos para métodos de classe (caso o código Java esteja compilado com javac -parameters).
  • Suporte Kotlin.
  • Suporte a membro da interface estática/padrão (DIM).
  • Suporte para anotações de tipo de referência anulável em Java (NRT).

AndroidCodegenTarget

XamarinAndroid já não é uma opção válida para a propriedade $(AndroidCodegenTarget). XAJavaInterop1 é agora a opção padrão e apenas suportada.

Se tiver código manualmente vinculado nos seus ficheiros Additions que interage com o código de vinculação gerado, talvez seja necessário atualizá-lo para ser compatível com XAJavaInterop1.

Inclusão de arquivo padrão

Dada a seguinte estrutura de ficheiros:

Transforms/
    Metadata.xml
foo.jar

Transforms\*.xml arquivos são incluídos automaticamente como um item @(TransformFile), e .jar/.aar arquivos são automaticamente incluídos como um item @(AndroidLibrary). Isso associará os tipos C# aos tipos Java encontrados no foo.jar usando as correções de metadados do Transforms\Metadata.xml.

O comportamento padrão de globbing de ficheiros relacionado ao Android é definido em AutoImport.props. Esse comportamento pode ser desabilitado para itens Android definindo a propriedade como $(EnableDefaultAndroidItems), ou todo o false comportamento padrão de inclusão de item pode ser desabilitado definindo a $(EnableDefaultItems) propriedade como false.

Arquivos .jar ou .aar indesejados podem ser incluídos com os curingas padrão. Por exemplo, os seguintes erros do compilador C# resultam de um AndroidStudio\gradle\wrapper\gradle-wrapper.jar arquivo sendo vinculado 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 resolver esse problema, você pode remover o arquivo específico em seu arquivo de projeto:

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

Como alternativa, você pode excluir todos os arquivos dentro de uma pasta:

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

Novos nomes de grupos de itens

<AndroidLibrary> é agora o grupo de itens recomendado a ser usado para todos os arquivos .jar e .aar. No Xamarin.Android, foram usados os seguintes grupos de itens, que podem usar metadados de itens para obter o mesmo resultado:

Grupo de itens herdados Novo Grupo de Itens Metadados do item Tipo de projeto legado
AndroidAarLibrary AndroidLibrary Bind="false" Aplicação
AndroidJavaLibrary AndroidLibrary Bind="false" Biblioteca de aplicativos ou classes
EmbeddedJar AndroidLibrary não aplicável Projeto de vinculação
EmbeddedReferenceJar AndroidLibrary Bind="false" Projeto de vinculação
InputJar AndroidLibrary Pack="false" Projeto de vinculação
LibraryProjectZip AndroidLibrary não aplicável Projeto de vinculação

Considere um .aar arquivo ou .jar no qual você não está interessado em incluir uma associação C#. Isto é comum para casos em que se têm dependências Java ou Kotlin que não é necessário chamar a partir de C#. Nesse caso, você pode definir os Bind metadados como false. Por padrão, o arquivo é coletado pelos curingas padrão. Você também pode usar o Update atributo para definir os Bind metadados:

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

Em um projeto de biblioteca de classes Android, isso redistribuiria o .jar arquivo dentro do pacote NuGet resultante como está. Em um projeto de aplicativo Android, isso incluiria o arquivo .jar no arquivo resultante .apk ou .aab. Nenhum dos dois incluiria uma ligação C# para esta biblioteca Java.

Arquivos JAR/AAR incorporados

No Xamarin.Android, o Java .jar ou .aar foi muitas vezes incorporado na ligação .dll como um recurso incorporado. No entanto, isso levou a compilações lentas, pois cada um deve ser aberto e verificado para encontrar código Java. Se encontrado, ele deve ser extraído para o disco para ser usado.

No .NET, o código Java não está mais incorporado no .dll. O processo de compilação da aplicação incluirá automaticamente quaisquer arquivos .jar ou .aar que encontrar no mesmo diretório que um .dll referenciado.

Se um projeto fizer referência a uma vinculação via <PackageReference> ou <ProjectReference> então tudo funciona e nenhuma consideração adicional é necessária. No entanto, se um projeto fizer referência a uma vinculação via <Reference>, o .jar/.aar deve estar localizado ao lado do .dll. Ou seja, para a seguinte referência:

<Reference Include="MyBinding.dll" />

Um diretório como o do exemplo a seguir não funcionará:

lib/
    MyBinding.dll

Em vez disso, o diretório também deve conter o código nativo:

lib/
    MyBinding.dll
    mybinding.jar

Considerações sobre migração

Há vários novos recursos definidos por padrão para ajudar a produzir ligações que melhor correspondam às suas contrapartes Java. No entanto, se você estiver migrando um projeto de vinculação existente, esses recursos podem criar associações que não são compatíveis com a API com suas associações existentes. Para manter a compatibilidade, você pode desativar ou modificar esses novos recursos.

Constantes de interface

Tradicionalmente, o C# não permite que constantes sejam declaradas em um interface, que é um padrão comum em Java:

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

Esse padrão foi suportado anteriormente pela criação de uma alternativa class que contém as constantes:

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

Com o C# 8, essas constantes são colocadas no interface.

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

No entanto, isso significa que a classe alternativa da qual o código existente pode depender não é mais gerada.

Definir a $(AndroidBoundInterfacesContainConstants) propriedade como false em seu arquivo de projeto reverterá para o comportamento herdado.

Tipos de interface aninhados

Tradicionalmente, o C# não permite que tipos aninhados sejam declarados em um interface, o que é permitido em Java:

public interface Foo {
     public class Bar { }
}

Esse padrão foi suportado movendo o tipo aninhado para um tipo de nível superior com um nome gerado composto pela interface e nome do tipo aninhado:

public interface IFoo { }

public class IFooBar : Java.Lang.Object { }

Com o C# 8, os interface tipos aninhados podem ser colocados no:

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

No entanto, isso significa que a classe de nível superior da qual o código existente pode depender não é mais gerada.

Definir a $(AndroidBoundInterfacesContainTypes) propriedade como false em seu arquivo de projeto reverterá para o comportamento herdado.

Se desejar usar uma abordagem híbrida, por exemplo, para manter os tipos aninhados existentes movidos para um tipo de nível superior, mas permitir que quaisquer tipos aninhados futuros permaneçam aninhados, você pode especificar isso no interface nível usando metadata para definir o unnest atributo. Defini-lo como true resultará em "desaninhamento" de quaisquer tipos aninhados (comportamento herdado):

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

Definir como false resultará em tipos aninhados permanecendo aninhados no interface (comportamento .NET):

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

Usando esta abordagem, pode deixar a $(AndroidBoundInterfacesContainTypes) propriedade como true e definir unnest como true para cada interface com tipos aninhados que tem atualmente. Estes permanecerão sempre como tipos de nível superior, enquanto quaisquer novos tipos introduzidos posteriormente serão estruturados de forma aninhada.

Membros de interface estáticos e padrão (DIM)

Tradicionalmente, o C# não permite que as interfaces contenham static membros e default métodos:

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

Membros estáticos nas interfaces têm sido suportados movendo-os para uma estrutura semelhante class.

public interface IFoo { }

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

default os métodos de interface tradicionalmente não são vinculados, uma vez que não são necessários e não havia uma construção em C# para suportá-los.

Com C# 8, static e default membros são suportados em interfaces, semelhante à interface Java.

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

No entanto, isso significa que o irmão class alternativo contendo static membros não será mais gerado.

Definir a $AndroidBoundInterfacesContainStaticAndDefaultInterfaceMethods propriedade como false em seu arquivo de projeto reverterá para o comportamento herdado.

Tipos de referência anuláveis

Suporte para Nullable Reference Types (NRT) foi adicionado no Xamarin.Android 11.0. O suporte NRT pode ser habilitado usando o mecanismo .NET padrão:

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

Como o padrão para .NET é disable, o mesmo se aplica para projetos Xamarin.Android.

Resource.designer.cs

No Xamarin.Android, os projetos de vinculação Java não suportavam a geração de um Resource.designer.cs arquivo. Como os projetos de vinculação são apenas bibliotecas de classes no .NET, esse arquivo será gerado. Isso pode ser uma mudança significativa ao migrar projetos existentes.

Um exemplo de falha dessa alteração é se sua associação gerar uma classe nomeada Resource no namespace raiz:

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

Ou, no caso do AndroidX, existem arquivos de projeto com - o nome, como androidx.window/window-extensions.csproj. Isso resulta no namespace window-extensions raiz e C# inválido em 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 desativar a geração de Resource.designer.cs, defina a propriedade $(AndroidGenerateResourceDesigner) como false no seu arquivo de projeto.

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