Condividi tramite


Procedura dettagliata: Associare una libreria Android Kotlin

Importante

Attualmente si sta esaminando l'utilizzo dell'associazione personalizzata nella piattaforma Xamarin. Si prega di prendere questo sondaggio per informare i futuri sforzi di sviluppo.

Xamarin consente agli sviluppatori di dispositivi mobili di creare app per dispositivi mobili native multipiattaforma usando Visual Studio e C#. È possibile usare i componenti android platform SDK predefiniti, ma in molti casi si vogliono usare anche SDK di terze parti scritti per tale piattaforma e Xamarin consente di eseguire questa operazione tramite associazioni. Per incorporare un framework Android di terze parti nell'applicazione Xamarin.Android, è necessario creare un binding Xamarin.Android per poterlo usare nelle applicazioni.

La piattaforma Android, insieme ai linguaggi nativi e agli strumenti, è in continua evoluzione, inclusa l'introduzione recente del linguaggio Kotlin, che alla fine viene impostata per sostituire Java. Esistono diversi SDK di 3d party, che sono già stati migrati da Java a Kotlin e presenta nuove sfide. Anche se il processo di associazione Kotlin è simile a Java, richiede passaggi aggiuntivi e impostazioni di configurazione per compilare ed eseguire correttamente come parte di un'applicazione Xamarin.Android.

L'obiettivo di questo documento è delineare un approccio generale per affrontare questo scenario e fornire una guida dettagliata con un semplice esempio.

Background

Kotlin è stato rilasciato a febbraio 2016 ed è stato posizionato come alternativa al compilatore Java standard in Android Studio entro il 2017. Più avanti nel 2019, Google ha annunciato che il linguaggio di programmazione Kotlin sarebbe diventato il linguaggio preferito per gli sviluppatori di app Android. L'approccio di associazione generale è simile al processo di associazione delle normali librerie Java con alcuni importanti passaggi specifici di Kotlin.

Prerequisiti

Per completare questo scenario, saranno necessari gli elementi seguenti:

Creare una libreria nativa

Il primo passaggio consiste nel creare una libreria Kotlin nativa usando Android Studio. La libreria viene in genere fornita da uno sviluppatore di terze parti o disponibile nel repository Maven di Google e in altri repository remoti. In questa esercitazione, ad esempio, viene creata un'associazione per la libreria di selezione bolle Kotlin:

GitHub BubblePicker demo

  1. Scaricare il codice sorgente da GitHub per la libreria e decomprimerlo in una cartella locale Bubble-Picker.

  2. Avviare Android Studio e selezionare Apri un'opzione di menu del progetto Android Studio esistente scegliendo la cartella locale Bubble-Picker:

    Android Studio Open Project

  3. Verificare che Android Studio sia aggiornato, incluso Gradle. Il codice sorgente può essere compilato correttamente in Android Studio v3.5.3, Gradle v5.4.1. Le istruzioni su come aggiornare Gradle alla versione più recente di Gradle sono disponibili qui.

  4. Verificare che Android SDK richiesto sia installato. Il codice sorgente richiede Android SDK v25. Aprire l'opzione di menu Strumenti > SDK Manager per installare i componenti SDK.

  5. Aggiornare e sincronizzare il file di configurazione build.gradle principale che si trova nella radice della cartella del progetto:

    • Impostare la versione Kotlin su 1.3.10

      buildscript {
          ext.kotlin_version = '1.3.10'
      }
      
    • Registrare il repository Maven predefinito di Google in modo che la dipendenza della libreria di supporto possa essere risolta:

      allprojects {
          repositories {
              jcenter()
              maven {
                  url "https://maven.google.com"
              }
          }
      }
      
    • Dopo aver aggiornato il file di configurazione, non è sincronizzato e Gradle mostra il pulsante Sincronizza ora , premere il file e attendere il completamento del processo di sincronizzazione:

      Android Studio Gradle Sync Now

      Suggerimento

      La cache delle dipendenze di Gradle può essere danneggiata, a volte si verifica dopo un timeout della connessione di rete. Scaricare nuovamente le dipendenze e sincronizzare il progetto (richiede la rete).

      Suggerimento

      Lo stato di un processo di compilazione Gradle (daemon) potrebbe essere danneggiato. L'arresto di tutti i daemon Gradle può risolvere questo problema. Arrestare i processi di compilazione Gradle (richiede il riavvio). Nel caso di processi Gradle danneggiati, è anche possibile provare a chiudere l'IDE e quindi a eliminare tutti i processi Java.

      Suggerimento

      Il progetto potrebbe usare un plug-in di terze parti, che non è compatibile con gli altri plug-in nel progetto o la versione di Gradle richiesta dal progetto.

  6. Aprire il menu Gradle a destra, passare al menu attività bubblepicker>, eseguire l'attività di compilazione toccandola due volte e attendere il completamento del processo di compilazione:

    Android Studio Gradle Execute Task

  7. Aprire il browser dei file della cartella radice e passare alla cartella di compilazione: Bubble-Picker - bubblepicker -> build ->> outputs -> aar, salvare il file bubblepicker-release.aar come bubblepicker-v1.0.aar, questo file verrà usato più avanti nel processo di associazione:

    Android Studio AAR Output

Il file AAR è un archivio Android, che contiene il codice sorgente e gli asset Kotlin compilati, richiesti da Android per eseguire un'applicazione usando questo SDK.

Preparare i metadati

Il secondo passaggio consiste nel preparare il file di trasformazione dei metadati, usato da Xamarin.Android per generare le rispettive classi C#. Un progetto di associazione Xamarin.Android rileverà tutte le classi e i membri nativi da un archivio Android specificato generando successivamente un file XML con i metadati appropriati. Il file di trasformazione dei metadati creato manualmente viene quindi applicato alla baseline generata in precedenza per creare il file di definizione XML finale usato per generare il codice C#.

I metadati usano la sintassi XPath e vengono utilizzati dal generatore di associazioni per influenzare la creazione dell'assembly di associazione. L'articolo Metadati binding Java fornisce altre informazioni sulle trasformazioni, che possono essere applicate:

  1. Creare un file di Metadata.xml vuoto:

    <?xml version="1.0" encoding="UTF-8"?>
    <metadata>
    </metadata>
    
  2. Definire le trasformazioni xml:

  • La libreria Kotlin nativa ha due dipendenze, che non si vuole esporre al mondo C#, definire due trasformazioni per ignorarle completamente. È importante dire che i membri nativi non verranno rimossi dal file binario risultante, solo le classi C# non verranno generate. Il decompiler Java può essere usato per identificare le dipendenze. Eseguire lo strumento e aprire il file AAR creato in precedenza, di conseguenza verrà visualizzata la struttura dell'archivio Android, riflettendo tutte le dipendenze, i valori, le risorse, il manifesto e le classi:

    Java Decompiler Dependencies

    Le trasformazioni per ignorare l'elaborazione di questi pacchetti vengono definite usando le istruzioni XPath:

    <remove-node path="/api/package[starts-with(@name,'org.jbox2d')]" />
    <remove-node path="/api/package[starts-with(@name,'org.slf4j')]" />
    
  • La classe nativa BubblePicker ha due metodi getBackgroundColor e setBackgroundColor la trasformazione seguente lo modificherà in una proprietà 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>
    
  • I tipi UInt, UShort, ULong, UByte non firmati richiedono una gestione speciale. Per questi tipi Kotlin modifica automaticamente i nomi dei metodi e i tipi di parametri, che si riflette nel codice generato:

    public open fun fooUIntMethod(value: UInt) : String {
        return "fooUIntMethod${value}"
    }
    

    Questo codice viene compilato nel codice di byte Java seguente:

    @NotNull
    public String fooUIntMethod-WZ4Q5Ns(int value) {
    return "fooUIntMethod" + UInt.toString-impl(value);
    }
    

    Inoltre, anche i tipi correlati come UIntArray, UShortArray, ULongArray, UByteArray sono interessati da Kotlin. Il nome del metodo viene modificato in modo da includere un suffisso aggiuntivo e i parametri vengono modificati in una matrice di elementi di versioni firmate degli stessi tipi. Nell'esempio seguente un parametro di tipo UIntArray viene convertito automaticamente in int[] e il nome del metodo viene modificato da fooUIntArrayMethod a fooUIntArrayMethod--ajY-9A. Quest'ultimo viene individuato dagli strumenti Xamarin.Android e generato come nome di metodo valido:

    public open fun fooUIntArrayMethod(value: UIntArray) : String {
        return "fooUIntArrayMethod${value.size}"
    }
    

    Questo codice viene compilato nel codice di byte Java seguente:

    @NotNull
    public String fooUIntArrayMethod--ajY-9A(@NotNull int[] value) {
        Intrinsics.checkParameterIsNotNull(value, "value");
        return "fooUIntArrayMethod" + UIntArray.getSize-impl(value);
    }
    

    Per assegnare un nome significativo, è possibile aggiungere i metadati seguenti alla Metadata.xml, che aggiornerà il nome in origine definito nel codice Kotlin:

    <attr path="/api/package[@name='com.microsoft.simplekotlinlib']/class[@name='FooClass']/method[@name='fooUIntArrayMethod--ajY-9A']" name="managedName">fooUIntArrayMethod</attr>
    

    Nell'esempio BubblePicker non sono presenti membri che usano tipi non firmati, pertanto non sono necessarie modifiche aggiuntive.

  • Membri Kotlin con parametri generici per impostazione predefinita trasformati in parametri di Java.Lang.Object digitare. Ad esempio, un metodo Kotlin ha un parametro generico <T>:

    public open fun <T>fooGenericMethod(value: T) : String {
    return "fooGenericMethod${value}"
    }
    

    Una volta generata un'associazione Xamarin.Android, il metodo viene esposto a C# come indicato di seguito:

    [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);
    

    I generics Java e Kotlin non sono supportati dalle associazioni Xamarin.Android, pertanto viene creato un metodo C# generalizzato per accedere all'API generica. Come soluzione alternativa, è possibile creare una libreria wrapper Kotlin ed esporre le API necessarie in modo fortemente tipizzato senza generics. In alternativa, è possibile creare helper sul lato C# per risolvere il problema nello stesso modo tramite API con tipi sicuri.

    Suggerimento

    Trasformando i metadati, eventuali modifiche possono essere applicate all'associazione generata. L'articolo Binding Java Library illustra in dettaglio come vengono generati ed elaborati i metadati.

Creare una libreria di associazioni

Il passaggio successivo consiste nel creare un progetto di associazione Xamarin.Android usando il modello di associazione di Visual Studio, aggiungere metadati necessari, riferimenti nativi e quindi compilare il progetto per produrre una libreria di consumo:

  1. Aprire Visual Studio per Mac e creare un nuovo progetto libreria di binding Xamarin.Android, assegnargli un nome, in questo caso testBubblePicker.Binding e completare la procedura guidata. Il modello di associazione Xamarin.Android si trova nel percorso seguente: Libreria di binding della libreria >Android>:

    Visual Studio Create Binding

    Nella cartella Trasformazioni sono presenti tre file di trasformazione principali:

    • Metadata.xml: consente di apportare modifiche all'API finale, ad esempio la modifica dello spazio dei nomi dell'associazione generata.
    • EnumFields.xml: contiene il mapping tra le costanti int Java e le enumerazioni C#.
    • EnumMethods.xml: consente di modificare i parametri del metodo e restituire tipi da costanti Java int a enumerazioni C#.

    Mantenere vuoti i file di EnumFields.xml e EnumMethods.xml e aggiornare il Metadata.xml per definire le trasformazioni.

  2. Sostituire il file trasformazioni/Metadata.xml esistente con il file Metadata.xml creato nel passaggio precedente. Nella finestra delle proprietà verificare che l'azione di compilazione del file sia impostata su TransformationFile:

    Visual Studio Metadata

  3. Aggiungere il file bubblepicker-v1.0.aar compilato nel passaggio 1 al progetto di associazione come riferimento nativo. Per aggiungere riferimenti alla libreria nativa, aprire il finder e passare alla cartella con l'archivio Android. Trascinare e rilasciare l'archivio nella cartella Jars in Esplora soluzioni. In alternativa, è possibile usare l'opzione Aggiungi menu di scelta rapida nella cartella Jars e scegliere File esistenti.... Scegliere di copiare il file nella directory ai fini di questa procedura dettagliata. Assicurarsi di verificare che l'azione di compilazione sia impostata su LibraryProjectZip:

    Visual Studio Native Reference

  4. Aggiungere un riferimento al pacchetto NuGet Xamarin.Kotlin.StdLib. Questo pacchetto è un'associazione per la libreria standard Kotlin. Senza questo pacchetto, l'associazione funzionerà solo se la libreria Kotlin non usa tipi specifici di Kotlin, altrimenti tutti questi membri non verranno esposti a C# e qualsiasi app che tenta di utilizzare l'associazione si arresterà in modo anomalo in fase di esecuzione.

    Suggerimento

    A causa di una limitazione di Xamarin.Android, gli strumenti di associazione possono essere aggiunti solo un singolo archivio Android (AAR) per ogni progetto di associazione. Se è necessario includere più file AAR, sono necessari più progetti Xamarin.Android, uno per ogni AAR. Se questo fosse il caso di questa procedura dettagliata, le quattro azioni precedenti di questo passaggio dovranno essere ripetute per ogni archivio. In alternativa, è possibile unire manualmente più archivi Android come singolo archivio e di conseguenza è possibile usare un singolo progetto di associazione Xamarin.Android.

  5. L'azione finale consiste nel compilare la libreria e non si verificano errori di compilazione. In caso di errori di compilazione, possono essere risolti e gestiti usando il file di Metadata.xml creato in precedenza aggiungendo metadati di trasformazione xml, che aggiungeranno, rimuoveranno o rinominano i membri della libreria.

Utilizzare la libreria di binding

Il passaggio finale consiste nell'usare la libreria di binding Xamarin.Android in un'applicazione Xamarin.Android. Creare un nuovo progetto Xamarin.Android, aggiungere un riferimento alla libreria di binding ed eseguire il rendering dell'interfaccia utente di Selezione bolle:

  1. Creare un progetto Xamarin.Android. Usare l'app Android per> Android > come punto di partenza e selezionare l'opzione Latest and Greatest as you Target Platforms (Più recente e più grande come destinazione delle piattaforme) per evitare problemi di compatibilità. Tutti i passaggi seguenti hanno come destinazione questo progetto:

    Visual Studio Create App

  2. Aggiungere un riferimento al progetto di associazione o aggiungere un riferimento alla DLL creata in precedenza:

    Visual Studio Add Binding Reference.png

  3. Aggiungere un riferimento al pacchetto NuGet Xamarin.Kotlin.StdLib aggiunto al progetto di associazione Xamarin.Android in precedenza. Aggiunge il supporto a qualsiasi tipo specifico di Kotlin che richiede la consegna in fase di esecuzione. Senza questo pacchetto l'app può essere compilata, ma si arresterà in modo anomalo in fase di esecuzione:

    Visual Studio Add StdLib NuGet

  4. Aggiungere il BubblePicker controllo al layout Android per MainActivity. Aprire il file testBubblePicker/Resources/layout/content_main.xml e aggiungere il nodo di controllo BubblePicker come ultimo elemento del controllo RelativeLayout radice:

    <?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>
    
  5. Aggiornare il codice sorgente dell'app e aggiungere la logica di inizializzazione a MainActivity, che attiva Bubble Picker SDK:

    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 e BubblePickerListener sono due classi da creare da zero, che gestiscono i dati delle bolle e l'interazione di controllo:

    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();
        }
    }
    
  6. Eseguire l'app, che deve eseguire il rendering dell'interfaccia utente di Selezione bolle:

    BubblePicker demo

    L'esempio richiede codice aggiuntivo per eseguire il rendering dello stile degli elementi e gestire le interazioni, ma il BubblePicker controllo è stato creato e attivato correttamente.

Complimenti. È stata creata correttamente un'app Xamarin.Android e una libreria di binding che usa una libreria Kotlin.

A questo punto dovrebbe essere disponibile un'applicazione Xamarin.Android di base che usa una libreria Kotlin nativa tramite una libreria di binding Xamarin.Android. Questa procedura dettagliata usa intenzionalmente un esempio di base per evidenziare meglio i concetti chiave introdotti. Negli scenari reali, è probabile che sia necessario esporre un numero maggiore di API e applicare le trasformazioni dei metadati.