Contattaci

Applicazioni native Android e iOS

  • Data: 23 Settembre 2020
  • Autore: Gabriele Coppola
  • Categorie

  • Giuneco Tech

    Il nostro settore richiede uno studio continuo e una forte attenzione all’innovazione. Incentiviamo quindi dei programmi formativi annuali per tutti i componenti del team, con ore dedicate (durante l’orario di lavoro) e serate formative sia online che in presenza. Sponsorizziamo eventi, sia come partner che semplicemente come partecipanti, e scriviamo articoli su quello che abbiamo imparato per essere, a nostra volta, dei divulgatori.

    Vai alla sezione Team
  • La Scelta della Tecnologia

    Quando si tratta di sviluppare un’applicazione mobile, uno dei dilemmi più grossi è scegliere la tecnologia con cui iniziare e portare a termine il progetto. Tipicamente le aziende scelgono una soluzione consona con il loro know-how. Aziende che hanno un reparto ICT di sviluppo  strutturato con una forte componente front-end, approcciano il problema dal loro punto di vista, andando a sviluppare la loro applicazione ibrida con Framework come React Native o Ionic. D’altro canto, aziende con sviluppatori .NET si trovano avvantaggiate avendo a disposizione il framework Xamarin, sia Platform che Forms, che li aiuta non solo nella scrittura, in quanto si trovano ad utilizzare C# e .NET Core, ben noti a loro, ma anche nel time to market, sviluppando in un colpo solo (con i dovuti accorgimenti) applicazioni native iOS e Android. Per completezza diciamo che oggi esiste anche un altro framework cross platform con le potenzialità di Xamarin, ovvero Flutter[1], sviluppato da Google, che fa uso del linguaggio Dart per lo sviluppo di app native.

    E allora, con la disponibilità di tutti questi framework, perché andare a scrivere un’applicazione nativa nel vero senso della parola, ossia con le tecnologie native che sono state concepite ad hoc per lo sviluppo mobile? Di seguito (e nel mio prossimo articolo), andremo a vedere come si strutturano queste tecnologie e quali sono gli strumenti messi a disposizione da Google ed Apple per scrivere applicazioni native iOS e Android.

    In questo articolo parleremo di come si differenzia lo sviluppo di applicazioni Android native rispetto alla controparte Xamarin, utilizzando il linguaggio Kotlin, sviluppato da JetBrains[2], insieme all’IDE di Google Android Studio.

    Kotlin – Linguaggio moderno concorrente di Java

    Come anticipato, Kotlin è linguaggio sviluppato da JetBrains a partire dal 2010 e successivamente reso open source nel 2012. È definito come general purpose, free, open source e pragmatico. Pragmatico in quanto cerca di distinguersi dalla struttura chiusa e ben definita di Java (e c#) al fine di permettere uno sviluppo più snello e veloce. Keyword come val, var e fun sono all’ordine del giorno in Kotlin; la prima dichiara una proprietà immutabile, la seconda una proprietà mutabile e la terza una funzione. Le function in particolare possono essere dichiarate all’inizio del file, dentro una classe, dentro altre function o come extension method a mo’ di C#. Insomma, in Kotlin si possono fare tante cose in tanti modi diversi. Costrutti “moderni” come funzioni di mapping, filtering o range sono inclusi in Kotlin, così come le lambda function, permettendo agli sviluppatori di eseguire query LINQ style.

    Una feature molto sponsorizzata di Kotlin è una Type Safety tale che permetta di evitare errori dovuti alla mancata inizializzazione delle variabili, che portano alla famosa NullPointerException di Java. Per esempio:

    Dichiarare una proprietà variabile intera come var counter: int per poi assegnarle il valore counter = null, farà si che il compilatore ci avvertirà staticamente dandoci un errore in fase di compilazione. Come in C#, possiamo introdurre il simbolo “?” per dichiarare nullabili le nostre variabili e permettere assegnazioni del tipo di cui sopra, oppure ignorare variabili null al bisogno. Per esempio, ciclando un array, è possibile posporre il simbolo “?”, alla variabile ciclata per ignorare quelle null:

    for (product in product)
    println(product?.Price)

    Come facciamo abitualmente in C#.

    In Kotlin possiamo anche creare dei Task attraverso le cosiddette Coroutines che potranno essere aspettate o meno, per fare eseguire il codice all’interno di esse in background oppure nel main thread.

    Sebbene Kotlin abbia ufficialmente supportato Android dal Google I/O del 2017, dal Google I/O del 2019 la stessa JetBrains definisce Android come Kotlin-First ed afferma:

    • Minor complessità a fronte di maggiore leggibilità;
    • Linguaggio ed ambiente maturo;
    • Viene supportato in Android JetPack (Suite di librerie core per Android orientata a supportare più versioni del SO e più device);
    • Interoperabilità con Java;
    • Supporto multipiattaforma e possibilità di sviluppare non sono software per backend, ma anche applicazioni web e iOS;
    • Code Safety, quindi facile da imparare e supportato da una grossa community.

    Ovviamente Kotlin è un linguaggio Object Oriented ed in quanto tale fa uso comune di classi, all’interno delle quali vigono delle coding convenctions: si dichiara prima la classe, poi si dichiarano le proprietà insieme alle loro inizializzazioni, poi i costruttori ed i metodi, infine i cosiddetti companion objects, ovvero le nostre classi statiche.

    Anche Google ha accettato Kotlin come linguaggio alternativo a Java per le applicazioni Android, tanto che, alla creazione di un nuovo progetto sull’IDE ufficiale Android Studio, ci viene chiesto se optare per Java o Kotlin e sulla documentazione online di Android ci sono sezioni ad hoc su come implementare il codice nei due linguaggi.

    Struttura Solution in Android Studio

    Come abbiamo visto dall’immagine precedente, creando un nuovo progetto ci viene chiesto, come di consueto, il nome del package della nostra applicazione, la location dove creare la cartella della solution, il linguaggio, il minimum SDK (ovvero il minumum target framework) per cui vogliamo che la nostra applicazione sia compatibile ed infine l’utilizzo o meno delle Android.Support libraries, ad oggi in fase di smantellamento a favore nelle librerie di AndroidX.

    Prima ancora di questo ci verrà chiesto se partire da una solution vuota o con, già impostato, un set minimo di navigazione e activities. Per il nostro esempio partiremo da una situazione pulita e man mano aggiungeremo le classi che ci serviranno.

    Vediamo subito che la struttura della solution è un po’ diversa da quella offerta da VisualStudio o Rider.

    Innanzitutto, possiamo dire che Android Studio ragiona in moduli; questi sono un insieme di sorgenti e configurazioni di build che permettono di dividere la solution in unità funzionali (un po’ come i progetti all’interno delle solution di in C#). Vedremo che per quanto riguarda le app, ci sarà by scaffolding il modulo app. Ogni modulo ha una sua configurazione di build e una sua batteria di test, in modo da poterlo testare e mettere in run.

    In perfetto stile JetBrains, Android Studio espone un menu a tendina che permette di cambiare visualizzazione della struttura ad albero della Solution e possiamo notare subito le due visualizzazioni più comuni: Android e Project.

    Nella prima vedremo una struttura che non rispecchia lo stato del file system, in quanto è stata pensata per avere a colpo d’occhio un summary della nostra solution. Possiamo analizzare di seguito le cartelle più importanti:

    • Gradle Scripts: mostra tutti i file di configurazione di build, per progetto e per moduli;
    • manifests: mostra tutti i file di manifest, per progetto e per moduli;
    • res: mostra tutte le risorse raggruppate.

    Nella seconda invece vedremo l’effettiva struttura della solution nel file system, comprensiva di cartelle e file tipicamente nascosti nella visualizzazione Android. Alcune delle più importanti sono:

    • build: contiene i file risultanti dalla build;
    • libs: contiene librerie private;
    • src: contiene tutto il codice ed i file di risorsa per fare build e run del progetto.
    • gradle: contiene le configurazioni di build del progetto (se guardiamo la cartella più esterna nel file system) o di modulo (se guardiamo le cartelle all’interno dei moduli).

    Vediamo, nel prossimo paragrafo, come fare un semplice esempio di navigazione a tab, paragonandolo con Xamarin.Android.

    Navigazione a tab

    Per creare una navigazione a tab, creiamo nella cartella res una sottocartella layout e, tramite il supporto di Android Studio, creiamo una nuova activity di tipo Bottom Navigation Activity.

    Vediamo come l’IDE ci suggerisce il nome ed il linguaggio da utilizzare per questo file.

    Il risultato che otteniamo è uno scaffold della main activity, che funge da startup dell’applicazione, insieme a tre fragment, ovvero delle partial in cui possiamo definire il contenuto dei tab.

    Notiamo come la visualizzazione di default di Android Studio per quanto riguarda i file xml (axml in Xamarin.Android), sia la versione grafica, Xcode style. In questo modo è possibile utilizzare la palette di controlli grafici ed inserirli nei nostri fragment tramite drag and drop. In questo caso se guardiamo la visualizzazione a codice, possiamo notare che sarebbe lo stesso della corrispettiva versione in Xamarin Android, questo perché entrambi gli ambienti ragionano di xml per descrivere le viste. Spostandoci nelle classi sottostanti le viste, ovvero le activity vere e proprie (curioso il fatto che si trovino nella cartella java, nonostante il linguaggio scelto sia Kotlin), possiamo notare che sono costruite con una trasposizione molto simile alla versione Xamarin.Android e di conseguenza alla versione Java. Ovviamente la sintassi è quella di Kotlin.

    Possiamo infatti vedere la MainActivity definita come class, al cui interno possiamo trovare la function in override onCreate (che viene chiamata allo startup dell’applicazione). Al suo interno vengono dichiarate ed inizializzate la view che definisce in tab in basso (nav_view) e la view che contiene i fragment (nav_host_fragment). Successivamente vengono dichiarati i tre tab da inserire nella tab bar. Gli id inseriti corrispondo a quelli definiti nel file bottom_nav_menu.xml costruito dallo scaffold.

    Se guardiamo invece com’è fatta la classe Kotlin di un fragment, vediamo come già di scaffold viene fatto uso della classe ViewModel di Java. Questa classe si occupa di tenere aggiornato lo stato del fragment ed è utilizzata per creare un binding notificante con la vista per far visualizzare le informazioni del testo contenute al suo interno. Da notare la keywork “it” di kotlin, utilizzata per referenziare implicitamente il parametro   di Observer.

    Come ultimo passo della prima fase, per dire ad Android Studio che la nostra MainActivity è la principale, ovvero lo startup dell’applicazione, dobbiamo includere nel file di manifest le seguenti righe (che la dichiarano essere l’activity principale ed il launcher). Su Xamarin.Android potevamo agire sia così, che con gli appositi attributi sulla classe. Vediamo che con questo piccolo sforzo abbiamo ottenuto uno scaffold di un’applicazione con navigazione bottom tab.

    Liste e Chiamate HTTP

    In questo paragrafo definiremo  una lista tramite Kotlin ed  la alimenteremo con dati ricevuti dal web.

    Innanzitutto dobbiamo definire la lista graficamente nel xml del fragment in cui la vogliamo includere. Per semplicità la inseriamo nella home.

    Visto che, per rispettare le linee guida, utilizzeremo una recyclerview, dobbiamo includere il relativo pacchetto come dipendenza nel file build.gradle del modulo app, in questo modo:

    Possiamo notare la sottolineatura rossa nelle dependecies di com.android.support; questo perché le support libraries sono disponibili fino ad Android Pie, per quelle successive sono state implementate le librerie di AndroidX.

    A questo punto possiamo aggiungere, nell’xml del fragment home, la dichiarazione del recyclerview, analogamente a come avremmo fatto in Xamarin.Android, con l’unica differenza di dover importare le support libraries tramite nuget invece che manualmente esplicitarle nel file Build.gradle.

    Vediamo come il recyclerview abbia un id specifico. Questo ci permetterà di riprenderlo sul file .KT tramite meccanismo nativo di Android FindViewById e poterlo setuppare a nostro piacere, come di seguito.

    Dalle immagini precedenti possiamo vedere come nel HomeFragment abbiamo definito le principali strutture che servono alla recyclerview per disegnarsi e popolare i propri dati, ovvero L’Adapter, il Viewholder ed il LayoutManager. In particolare l’Adapter è la struttura

    che si occupa di gestire l’interazione del recyclerview con il viewholder ed il ciclo di vita di quest’ultimo. Il viewholder inserisce i dati nel recyclerview, mentre il layout manager istruisce il recyclerview su come disegnarsi. In questo momento, per semplicità, abbiamo usato un

    LinearLayoutManager di default, ma per item della lista la cui struttura ha bisogno di essere più complessa, possiamo definire dei LayoutManager custom. L’Adapter è generico su MyData, ovvero la struttura dati scelta per mantenere i dati da mostrare a schermo.

    Il risultato è una lista a scorrimento verticale i cui item sono composti da due textbox.

    Come passo successivo proviamo a collegare la lista sopra definita a dei dati ricevuti dal web tramite chiamata http. Per questo caso d’uso sfrutteremo la libreria Volley per Android per fare una richiesta http in get verso una delle tante webapi gratuite[3]

    Come di consueto, per includere librerie esterne andiamo ad aggiungere la relativa riga su file build.gradle. Oltre a volley abbiamo bisogno di un meccanismo per deserializare il messaggio ricevuto dalla richiesta nella classe che utilizzeremo per definire il viewholder. Questo meccanismo ci è dato dalla libreria di Google Gson.

    Nella figura a sinistra possiamo vedere come la definizione della classe GsonRequest, che estende Request di volley, ci aiuti a deserializzare il messaggio ed a restituirlo come valore di ritorno. In particolare il body della risosta viene settato nella variabile json che poi viene passata alla libreria gson per essere parsata. Il parametro clazz rappresenta il tipo su cui gson deve ricondurre il json.

    Ora possiamo istanziare la nostra GsonRequest passando url della richiesta e definendo la coda di richieste, passando il contesto del fragment; il contesto del fragment non è altro che l’activity sulla quale questo si appoggia. Se la coda fosse stata definita direttamente su un’activity, si sarebbe passata direttamente questa tramite this.

    Gli ultimi due parametri della Gson Request sono due action i cui body gestiscono rispettivamente il caso di successo ed il caso di errore. Per quanto riguarda il caso di successo, settiamo il risultato della chiamata nella variabile opportuna, per poi passarla all’adapter che gestisce la vista. Il risultato è il seguente.

    Pubblicazione

    Come facciamo in Xamarin.Android, anche in Android Studio possiamo generare APK o AAB tramite l’apposito tool dell’IDE e tramite il corretto file di KeyStore con gli opportuni alias e password. Per fare questo basta cliccare su Generate Signed Bundle or APK e scegliere APK o AAB.

    L’AAB è un nuovo meccanismo che Google ha introdotto per ridurre drasticamente le dimensioni del pacchetto di release, a scapito di non poterlo installare sui dispositivi direttamente, obbligandoci a passare dal Play Store.

    Generiamo od utilizziamo il keystore per la nostra applicazione inserendo alias, password ed almeno uno dei campi nella sezione Certificate.

    Scegliamo l’ambiente di compilazione, il meccanismo di signature e clicchiamo su finish.
    In questo modo Android Studio farà la build del progetto nell’ambiente richiesto e genererà l’apk nella destinazione richiesta. L’apk ora potrà essere installato manualmente o tramite FTP direttamente sui dispositivi Android supportati, oppure potrà essere inviato al Play Store il quale lo analizzerà e lo distribuirà al pubblico in base al canale di distribuzione scelto.

    Conclusioni

    Oltre alle difficoltà fisiologiche di interagire e fare i primi passi con un nuovo linguaggio, non ho trovato moltissime differenze fra la fra l’uso dei meccanismi di Xamarin.Android e di Android con Kotlin. Utilizzando quotidianamente Rider, Android Studio mi è risultato abbastanza familiare, al netto degli shortcut totalmente diversi. Probabilmente l’impatto di chi arriva da Visual Studio potrebbe essere più importante.

    Sebbene lo startup dell’applicazione sia assai più veloce rispetto alla corrispettiva versione Xamarin, non vedo il motivo di passare al nativo, se non esplicitamente richiesto dal cliente o per l’utilizzo obbligato di librerie native per cui siamo impossibilitati a generare una Xamarin Binding Library.


    [1] https://flutter.dev/

    [2] https://www.jetbrains.com/

    [3] https://rickandmortyapi.com/

    Designed by pikisuperstar / Freepik

    Designed by macrovector / Freepik