Passo a passo: associar uma biblioteca do Android Kotlin
Importante
No momento, estamos investigando o uso de associação personalizada na plataforma Xamarin. Faça esta pesquisa para informar os esforços futuros de desenvolvimento.
O Xamarin permite que os desenvolvedores móveis criem aplicativos móveis nativos multiplataforma usando o Visual Studio e o C#. Você pode usar os componentes do SDK da plataforma Android prontos para uso, mas, em muitos casos, também deseja usar SDKs de terceiros gravados para essa plataforma e o Xamarin permite que você faça isso por meio de associações. Para incorporar uma estrutura android de terceiros em seu aplicativo Xamarin.Android, você precisa criar uma associação Xamarin.Android para ela antes de usá-la em seus aplicativos.
A plataforma Android, juntamente com seus idiomas nativos e ferramentas, está em constante evolução, incluindo a recente introdução da linguagem Kotlin, que eventualmente está definida para substituir Java. Há vários SDKs de terceiros, que já foram migrados do Java para o Kotlin e nos apresenta novos desafios. Embora o processo de associação kotlin seja semelhante ao Java, ele requer etapas adicionais e configurações para compilar e executar com êxito como parte de um aplicativo Xamarin.Android.
O objetivo deste documento é descrever uma abordagem de alto nível para abordar esse cenário e fornecer um guia passo a passo detalhado com um exemplo simples.
Tela de fundo
Kotlin foi lançado em fevereiro de 2016 e foi posicionado como uma alternativa ao compilador Java padrão no Android Studio em 2017. Mais tarde, em 2019, o Google anunciou que a linguagem de programação Kotlin se tornaria sua linguagem preferida para desenvolvedores de aplicativos Android. A abordagem de associação de alto nível é semelhante ao processo de associação de bibliotecas Java regulares com algumas etapas específicas importantes do Kotlin.
Pré-requisitos
Para concluir este passo a passo, você precisará de:
Criar uma biblioteca nativa
A primeira etapa é criar uma biblioteca nativa do Kotlin usando o Android Studio. A biblioteca geralmente é fornecida por um desenvolvedor de terceiros ou está disponível no repositório Maven do Google e em outros repositórios remotos. Por exemplo, neste tutorial, é criada uma associação para a Biblioteca Kotlin do Seletor de Bolhas:
Baixe o código-fonte do GitHub para a biblioteca e descompacte-o em uma pasta local Seletor de Bolhas.
Inicie o Android Studio e selecione Abrir uma opção de menu de projeto existente do Android Studio escolhendo a pasta local Bubble-Picker:
Verifique se o Android Studio está atualizado, incluindo o Gradle. O código-fonte pode ser criado com êxito no Android Studio v3.5.3, Gradle v5.4.1. Instruções sobre como atualizar o Gradle para a versão mais recente do Gradle podem ser encontradas aqui.
Verifique se o SDK do Android necessário está instalado. O código-fonte requer o SDK do Android v25. Abra a opção de menu Gerenciador do SDK do Tools > para instalar componentes do SDK.
Atualize e sincronize o main arquivo de configuração build.gradle localizado na raiz da pasta do projeto:
Definir a versão kotlin como 1.3.10
buildscript { ext.kotlin_version = '1.3.10' }
Registre o repositório Maven padrão do Google para que a dependência da biblioteca de suporte possa ser resolvida:
allprojects { repositories { jcenter() maven { url "https://maven.google.com" } } }
Depois que o arquivo de configuração for atualizado, ele estará fora de sincronia e o Gradle mostrará o botão Sincronizar Agora , pressione-o e aguarde a conclusão do processo de sincronização:
Dica
O cache de dependência do Gradle pode estar corrompido, isso às vezes ocorre após um tempo limite de conexão de rede. Recarregar dependências e sincronizar projeto (requer rede).
Dica
O estado de um processo de build do Gradle (daemon) pode estar corrompido. Parar todos os daemons do Gradle pode resolver esse problema. Parar processos de build do Gradle (requer reinicialização). No caso de processos Gradle corrompidos, você também pode tentar fechar o IDE e, em seguida, eliminar todos os processos java.
Dica
Seu projeto pode estar usando um plug-in de terceiros, que não é compatível com os outros plug-ins no projeto ou com a versão do Gradle solicitada pelo projeto.
Abra o menu Gradle à direita, navegue até o menu Tarefas do seletor > de bolhas , execute a tarefa de build tocando duas vezes nele e aguarde a conclusão do processo de build:
Abra o navegador de arquivos de pasta raiz e navegue até a pasta de build: Bubble-Picker -> bubblepicker -> build -> outputs -> aar, salve o arquivo bubblepicker-release.aar como bubblepicker-v1.0.aar, esse arquivo será usado posteriormente no processo de associação:
O arquivo AAR é um arquivo android, que contém o código-fonte e os ativos do Kotlin compilados, exigidos pelo Android para executar um aplicativo usando esse SDK.
Preparar metadados
A segunda etapa é preparar o arquivo de transformação de metadados, que é usado pelo Xamarin.Android para gerar as respectivas classes C#. Um Projeto de Associação do Xamarin.Android descobrirá todas as classes nativas e membros de um determinado arquivo Android gerando posteriormente um arquivo XML com os metadados apropriados. O arquivo de transformação de metadados criado manualmente é aplicado à linha de base gerada anteriormente para criar o arquivo de definição XML final usado para gerar o código C#.
Os metadados usam a sintaxe XPath e são usados pelo Gerador de Associações para influenciar a criação do assembly de associação. O artigo Metadados de Associação Java fornece mais informações sobre transformações, que podem ser aplicadas:
Crie um arquivo deMetadata.xml vazio:
<?xml version="1.0" encoding="UTF-8"?> <metadata> </metadata>
Definir transformações xml:
A biblioteca kotlin nativa tem duas dependências, que você não deseja expor ao mundo C#, definem duas transformações para ignorá-las completamente. É importante dizer que os membros nativos não serão retirados do binário resultante, somente as classes C# não serão geradas. O Descompilador java pode ser usado para identificar as dependências. Execute a ferramenta e abra o arquivo AAR criado anteriormente, como resultado, a estrutura do arquivo morto do Android será mostrada, refletindo todas as dependências, valores, recursos, manifesto e classes:
As transformações para ignorar o processamento desses pacotes são definidas usando instruções XPath:
<remove-node path="/api/package[starts-with(@name,'org.jbox2d')]" /> <remove-node path="/api/package[starts-with(@name,'org.slf4j')]" />
A classe nativa
BubblePicker
tem dois métodosgetBackgroundColor
esetBackgroundColor
a seguinte transformação a transformará em uma propriedade C#BackgroundColor
:<attr path="/api/package[@name='com.igalata.bubblepicker.rendering']/class[@name='BubblePicker']/method[@name='getBackground' and count(parameter)=0]" name="propertyName">BackgroundColor</attr> <attr path="/api/package[@name='com.igalata.bubblepicker.rendering']/class[@name='BubblePicker']/method[@name='setBackground' and count(parameter)=1 and parameter[1][@type='int']]" name="propertyName">BackgroundColor</attr>
Tipos não assinados
UInt, UShort, ULong, UByte
exigem tratamento especial. Para esses tipos, o Kotlin altera os nomes de método e os tipos de parâmetros automaticamente, o que é refletido no código gerado:public open fun fooUIntMethod(value: UInt) : String { return "fooUIntMethod${value}" }
Esse código é compilado no seguinte código de byte Java:
@NotNull public String fooUIntMethod-WZ4Q5Ns(int value) { return "fooUIntMethod" + UInt.toString-impl(value); }
Além disso, tipos relacionados como
UIntArray, UShortArray, ULongArray, UByteArray
também são afetados pelo Kotlin. O nome do método é alterado para incluir um sufixo adicional e os parâmetros são alterados para uma matriz de elementos de versões assinadas dos mesmos tipos. No exemplo abaixo, um parâmetro do tipoUIntArray
é convertido automaticamente emint[]
e o nome do método é alterado defooUIntArrayMethod
parafooUIntArrayMethod--ajY-9A
. Este último é descoberto pelas ferramentas do Xamarin.Android e gerado como um nome de método válido:public open fun fooUIntArrayMethod(value: UIntArray) : String { return "fooUIntArrayMethod${value.size}" }
Esse código é compilado no seguinte código de byte Java:
@NotNull public String fooUIntArrayMethod--ajY-9A(@NotNull int[] value) { Intrinsics.checkParameterIsNotNull(value, "value"); return "fooUIntArrayMethod" + UIntArray.getSize-impl(value); }
Para dar a ele um nome significativo, os seguintes metadados podem ser adicionados ao Metadata.xml, que atualizará o nome de volta para originalmente definido no código Kotlin:
<attr path="/api/package[@name='com.microsoft.simplekotlinlib']/class[@name='FooClass']/method[@name='fooUIntArrayMethod--ajY-9A']" name="managedName">fooUIntArrayMethod</attr>
No exemplo bubblepicker, não há membros usando tipos não assinados, portanto, nenhuma alteração adicional é necessária.
Membros kotlin com parâmetros genéricos por padrão transformados em parâmetros de Java.
Lang.Object
Tipo. Por exemplo, um método Kotlin tem um parâmetro <genérico T>:public open fun <T>fooGenericMethod(value: T) : String { return "fooGenericMethod${value}" }
Depois que uma associação Xamarin.Android é gerada, o método é exposto ao C# conforme abaixo:
[Register ("fooGenericMethod", "(Ljava/lang/Object;)Ljava/lang/String;", "GetFooGenericMethod_Ljava_lang_Object_Handler")] [JavaTypeParameters (new string[] { "T" })] public virtual string FooGenericMethod (Java.Lang.Object value);
Os genéricos Java e Kotlin não são compatíveis com associações Xamarin.Android, portanto, um método C# generalizado para acessar a API genérica é criado. Como uma solução alternativa, você pode criar uma biblioteca Kotlin wrapper e expor as APIs necessárias de maneira forte sem genéricos. Como alternativa, você pode criar auxiliares no lado do C# para resolver o problema da mesma maneira por meio de APIs de tipo forte.
Dica
Ao transformar os metadados, todas as alterações podem ser aplicadas à associação gerada. O artigo Biblioteca Java de Associação explica em detalhes como os metadados são gerados e processados.
Criar uma biblioteca de associação
A próxima etapa é criar um projeto de associação Xamarin.Android usando o modelo de associação do Visual Studio, adicionar metadados necessários, referências nativas e, em seguida, criar o projeto para produzir uma biblioteca consumível:
Abra Visual Studio para Mac e crie um novo projeto da Biblioteca de Associação do Xamarin.Android, dê a ele um nome, nesse caso, testeBubblePicker.Binding e conclua o assistente. O modelo de associação Xamarin.Android está localizado pelo seguinte caminho: Biblioteca de Associação de Biblioteca > do Android>:
Na pasta Transformações, há três arquivos de transformação main:
- Metadata.xml – permite que alterações sejam feitas na API final, como alterar o namespace da associação gerada.
- EnumFields.xml – contém o mapeamento entre constantes java int e enumerações C#.
- EnumMethods.xml – permite alterar parâmetros de método e retornar tipos de constantes java int para enumerações C#.
Mantenha vazios os arquivos EnumFields.xml e EnumMethods.xml e atualize o Metadata.xml para definir suas transformações.
Substitua o arquivo transformations/Metadata.xml existente pelo arquivo Metadata.xml criado na etapa anterior. Na janela de propriedades, verifique se a ação de build do arquivo está definida como TransformationFile:
Adicione o arquivo bubblepicker-v1.0.aar que você criou na Etapa 1 ao projeto de associação como uma referência nativa. Para adicionar referências de biblioteca nativa, abra o localizador e navegue até a pasta com o arquivo morto do Android. Arraste e solte o arquivo na pasta Jars em Gerenciador de Soluções. Como alternativa, você pode usar a opção adicionar menu de contexto na pasta Jars e escolher Arquivos Existentes.... Escolha copiar o arquivo para o diretório para fins deste passo a passo. Verifique se a Ação de Build está definida como LibraryProjectZip:
Adicione uma referência ao pacote NuGet Xamarin.Kotlin.StdLib . Esse pacote é uma associação para a Biblioteca Padrão kotlin. Sem esse pacote, a associação só funcionará se a biblioteca Kotlin não usar tipos específicos do Kotlin, caso contrário, todos esses membros não serão expostos ao C# e qualquer aplicativo que tentar consumir a associação falhará em runtime.
Dica
Devido a uma limitação do Xamarin.Android, as ferramentas de associação apenas um único arquivo morto do Android (AAR) podem ser adicionadas por projeto de associação. Se vários arquivos AAR precisarem ser incluídos, vários projetos do Xamarin.Android serão necessários, um por cada AAR. Se esse fosse o caso para este passo a passo, as quatro ações anteriores desta etapa teriam que ser repetidas para cada arquivo morto. Como uma opção alternativa, é possível mesclar manualmente vários arquivos do Android como um único arquivo morto e, como resultado, você pode usar um único projeto de associação Xamarin.Android.
A ação final é criar a biblioteca e não ter erros de compilação. No caso de erros de compilação, eles podem ser abordados e tratados usando o arquivo Metadata.xml, que você criou anteriormente adicionando metadados de transformação xml, que adicionarão, removerão ou renomearão membros da biblioteca.
Consumir a biblioteca de associação
A etapa final é consumir a biblioteca de associação Xamarin.Android em um aplicativo Xamarin.Android. Crie um novo projeto Xamarin.Android, adicione referência à biblioteca de associação e renderize a interface do usuário do Seletor de Bolhas:
Criar projeto Xamarin.Android. Use o Aplicativo Android do Aplicativo > Android > como ponto de partida e selecione a opção Mais Recente e Maior como Você Direciona plataformas para evitar problemas de compatibilidade. Todas as etapas a seguir visam este projeto:
Adicione uma referência de projeto ao projeto de associação ou adicione uma referência à DLL criada anteriormente:
Adicione uma referência ao pacote NuGet Xamarin.Kotlin.StdLib que você adicionou ao projeto de associação Xamarin.Android anteriormente. Ele adiciona suporte a todos os tipos específicos do Kotlin que precisam ser entregues em runtime. Sem esse pacote, o aplicativo pode ser compilado, mas falhará em runtime:
Adicione o
BubblePicker
controle ao layout do Android paraMainActivity
. Abra o arquivo testBubblePicker/Resources/layout/content_main.xml e acrescente o nó de controle BubblePicker como o último elemento do controle RelativeLayout raiz:<?xml version="1.0" encoding="utf-8"?> <RelativeLayout …> … <com.igalata.bubblepicker.rendering.BubblePicker android:id="@+id/picker" android:layout_width="match_parent" android:layout_height="match_parent" app:backgroundColor="@android:color/white" /> </RelativeLayout>
Atualize o código-fonte do aplicativo e adicione a lógica de inicialização ao
MainActivity
, que ativa o SDK do Seletor de Bolhas:protected override void OnCreate(Bundle savedInstanceState) { ... var picker = FindViewById<BubblePicker>(Resource.Id.picker); picker.BubbleSize = 20; picker.Adapter = new BubblePickerAdapter(); picker.Listener = new BubblePickerListener(picker); ... }
BubblePickerAdapter
eBubblePickerListener
são duas classes a serem criadas do zero, que manipulam os dados de bolhas e controlam a interação:public class BubblePickerAdapter : Java.Lang.Object, IBubblePickerAdapter { private List<string> _bubbles = new List<string>(); public int TotalCount => _bubbles.Count; public BubblePickerAdapter() { for (int i = 0; i < 10; i++) { _bubbles.Add($"Item {i}"); } } public PickerItem GetItem(int itemIndex) { if (itemIndex < 0 || itemIndex >= _bubbles.Count) return null; var result = _bubbles[itemIndex]; var item = new PickerItem(result); return item; } } public class BubblePickerListener : Java.Lang.Object, IBubblePickerListener { public View Picker { get; } public BubblePickerListener(View picker) { Picker = picker; } public void OnBubbleDeselected(PickerItem item) { Snackbar.Make(Picker, $"Deselected: {item.Title}", Snackbar.LengthLong) .SetAction("Action", (Android.Views.View.IOnClickListener)null) .Show(); } public void OnBubbleSelected(PickerItem item) { Snackbar.Make(Picker, $"Selected: {item.Title}", Snackbar.LengthLong) .SetAction("Action", (Android.Views.View.IOnClickListener)null) .Show(); } }
Execute o aplicativo, que deve renderizar a interface do usuário do Seletor de Bolhas:
O exemplo requer código adicional para renderizar o estilo dos elementos e manipular interações, mas o
BubblePicker
controle foi criado e ativado com êxito.
Parabéns! Você criou com êxito um aplicativo Xamarin.Android e uma biblioteca de associação, que consome uma biblioteca Kotlin.
Agora você deve ter um aplicativo Xamarin.Android básico que usa uma biblioteca kotlin nativa por meio de uma biblioteca de associação Xamarin.Android. Este passo a passo usa intencionalmente um exemplo básico para enfatizar melhor os principais conceitos que estão sendo introduzidos. Em cenários do mundo real, provavelmente será necessário expor um número maior de APIs e aplicar transformações de metadados a elas.