Xamarin Android: Il grande mistero degli Android API Level – Prima parte

Tra i grandi misteri che l’intelletto propone io aggiungo senza dubbio la configurazione degli Android API Level nei progetti Xamarin.Android, nonchè i molti messaggi di errore che NUGet propone quando si modificano in modo improvvido detti valori, o comunque si traffica con le versioni dei pacchetti.

Io con franchezza ho grosse difficoltà a comprendere l’argomento, e tutte le volte che devo toccare qualcosa in questo ambito mi rifaccio ai miei “sacri” appunti e…. incrocio le dita……

Per questo motivo ho deciso di scriverne qualcosa con la speranza che possa aiutare anche altri.

Quanto qui riportato è relativo al mondo Xamarin, però la problematica riportata si ripropone in modo perfettamente analogo per ogni framework (compreso il nativo) che scrive applicazioni per Android.

Iniziamo con specificare i termini del problema.

Per ogni progetto Xamarin.Android è necessario impostare nelle proprietà del progetto i tre seguenti valori.

Minimum Android Version (minSdkVersion)
Questo valore permette di specificare la minima versione di Android dove la app è in grado di funzionare.

Target Framework
Indica quale versione di Android viene utilizzata per compilare la app.

Target Android Version (targetSdkVersion)
E’ la versione di Android dove si immagina che la nostra app sarà in esecuzione. In altri termini possiamo dire che la app sarà ottimizzata per la versione di Android qui specificata.

Il commento che ho specificato sotto ogni voce scommetto che più che chiarire Vi ha fatto confusione: concordo appieno ma abbiate la pazienza di leggere il seguito e tutto (forse) diventerà chiaro.

Dopo aver seguito tutte le osservazioni che Vi segnalo sono certo che comprenderete qualcosa in più di questi “mistemagici” valori.


>> Osservazione 1

I primi due valori (minSdkVersion e targetSdkVersion), pur venendo proposti nelle proprietà del progetto, in realtà servono per alimentare lo strafamoso file AndroidManifest.xml.

Per chi non lo sapesse questo file indica le carattaristiche della app, e viene inglobato nel pacchetto apk di installazione in modo tale che tutti gli attori (Google Play nonchè i device che stanno installando il pacchetto) siano informati circa le caratteristiche principali del software.

<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" 
android:versionName="1.0" package="com.companyname.Test01" android:installLocation="auto"> <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="27" />
...

Il Target Framework invece viene solo impostato nel file del progetto (<nome progetto>.csproj).

Tutto questo è ulteriormente confermato dal fatto che i valori minSdkVersion e targetSdkVersion sono presenti nella sezione delle proprietà del progetto Android Manifest, mentre il Target Framework è presente nella sezione Application.

>> Osservazione 2
A iniziare da Agosto 2018 le policy di Google obbligano per le nuove app che il targetSdkVersion debba essere al minimo impostato ad Android 8.0, pari ad API level 26 (vedere linkografia).

Più nello specifico a partire dal 01.08.2018 le app che non sono prepate con tale requisito semplicemente non possono essere caricate sul Google play store.

Quelle invece già presenti e che non rispettano tali requisiti a breve spariranno dalle ricerche.

>> Osservazione 3
E’ importante e banale evidenziare come in un modo perfetto il minSdkVersion dovrebbe essere impostato a un valore molto basso, con l’intento di rendere compatibile la nostra meravigliosa mobile-app sul maggiore numero possibile di dispositivi, mentre il targetSdkVersion sarebbe meglio valorizzato con un valore abbastanza alto in modo da favorire il funzionamento sui dispositivi in uso realmente.

Purtroppo, come Vi sarete accorti, non viviamo in un mondo perfetto, e il valore assegnato al minSdkVersion è molto critico e deve essere valutato con estrema attenzione.

Capiremo il perchè tra poco.

In ogni caso occorrerà sempre soddisfare la seguente disequazione.

minSdkVersion <= targetSdkVersion

>> Osservazione 4
Se state usando un dispositivo con una determinata versione di Android, navigando sul Google Play Store non saranno visibili le app che hanno un minSdkVersion successive a quello del Vs dispositivo.

Se tentate di fare i furbi e scaricate il file di installazione apk di una app che è stata creata con un minSdkVersion successivo a quello del dispositivo in uso e, facendo gli gnorri, ne tentate l’installazione, il sistema Vi farà una “sana leva” e bloccherà il processo.

>> Osservazione 5
Ad ogni versione di Android vengono aggiunte nuove funzionalità che si traducono, per noi programmatori, in nuove API o nuovi metodi da poter utilizzare.

>> Osservazione 6
Il target framework è l’SDK che sarà usato per compilare la app. Ovviamente il valore impostato deve avere riscontro negli SDK installati sulla Vostra macchina: in caso l’SDK manchi usando l’Android SKD Manager è possibile installarlo in pochi minuti (in visual studio: Tools->Android->Android SDK Manager).

Per il target framework deve valere la seguente disequazione.

targetSdkVersion <= target framework

Pertanto mettendo tutti insieme occorre che i tre valori soddisfino il seguente.

minSdkVersion <= targetSdkVersion <= target framework

>> Osservazione 7
Il valore impostato nel target framework influisce anche come il pacchetto di installazione apk viene creato.

Per questo motivo quando si imposta un valore nel target framework occorre verificare non solo che nell’Android SDK Manager la versione dell’SDK sia installata, ma che anche sia installato il relativo Build Tools (se non presente della stessa versione almeno quella più vicina).

Questo solo per evitare al massimo problematiche di installazione: infatti dalla mia esperienza anche creare apk con Build Tools meno recenti in generale non propone particolari problematiche.

Ma è bene evitare al massimo ogni rogna e quindi il mio consiglio è installare tutto: con pochi MB ci si leva da molte rogne.

>> Osservazione 8
Ma più in dettaglio quali sono gli scopi dei valori targetSdkVersion e target framework ?

Tutta la problematica che sta attorno a questi tre valori nasce dal fatto che escono regolarmente nuove versioni di Android, e ognuna di queste aggiunge nuove funzionalità alla versione precedente. Nasce il problema della retrocompatibilità: che propone svariate problematiche.

In generale tutte le funzioni presenti nelle vecchie revisioni sono mantenute nelle nuove, in modo tale che una app che andava meraviglisamente su un dispositivo con una certa versione di Android possa continuare a funzionare anche su un nuovo dispositivo con versioni di Android successiva.

Però può capitare (ed è capitato, in effetti) che alcune funzionalità possano essere eliminate magari perchè difettose o poichè ci si è accorto successivamente che introducono problemi inerenti la sicurezza.

C’è poi il problema dei template, o altri elementi grafici similari, che se applicati a versioni di app che non sono pronte ad accorgliele potrebbero portare a malfunzionamenti della stessa.

Quindi in definitiva per evitare che una app che funziona perfettamente in una versione di Android possa smettere di funzionare correttamente su versioni successive sono state introdotte alcuni meccanismi: ecco qui che entrano in gioco i due valori targetSdkVersion e target framework.

Mi preme osservare che quanto sopra viene acuito dal fatto che ora molti dispositivi si aggiornano OTA (=Over the Air), e quindi se in passato per cambiare versione di Android occorreva cambiare dispositivo, ora basta un semplice aggiornamento di pochi minuti.

Altra cosa da segnalare è che per aumentare la retrocompatiblità della nostra app, in modo tale da allargare al massimo l’utenza, un approccio potrebbe essere quello di tenere una basso valore di minSdkVersion e stare attenti a ogni istruzione utilizzata in modo tale di essere sicuri che queste siano sicuramente presenti in questa.

Questo metodo, seppur valido, penalizza la nostra app, che non potrà usufruire delle nuove caratteristiche messe a disposizione dalle versioni successive dell’SDK.

Quindi il targetSdkVersion indica allo sviluppatore quali API, e quindi quali istruzioni, sono disponibili. Più grezzamente se una istruzione è stata introdotta a partire da una versione di Android X, allora se non si imposta il valore targetSdkVersion >= X allora questa non sarà utilizzabile (si avrà errore in compilazione e l’IDE che utilizate dovrebbe segnalare un errore quando la digitate).

La cosa insidiosa è che questo controllo si esegue solo in fase di compilazione: se si tenta di installare la nostra app su un dispositivo Android con versione inferiore a X ecco che la cosa sarà perfettamente legale e possibile: come detto sopra basta che sia soddisfatta la segunete disequazione minSdkVersion <= targetSdkVersion.

Ora questo non vuole dire che capita qualche magia: significa semplicemente che quando la app tenta di eseguire l’istruzione non presente nella versione minSdkVersion si avrà un bel crash.

Quindi in definitiva è corretto dire che il targetSdkVersion  indica quali istruzioni sono potenzialmente disponibili: non capiterà alcuna magia se queste non sono disponibili nel dispositivo.

E… quindi ?? Che fare ?

Semplice: è compito del programmatore mettere degli IF nei punti critici in modo tale da fare una cosa del genere.

If (<Versione di Android in uso> >= X)
<Esegui istruzione presente solo dalla versione X>
else
<Implementazione alternativa di X>

L’implementazione alternativa dell’istruzione deve essere eseguita solo con istruzioni presenti e deve emulare in qualche modo l’istruzione mancante.

Quindi in questo modo abbiamo la possibilità di usare nei nuovi dispositivi le nuove istruzioni messe a disposizione da Android, e, con lo sforzo del programmatore, nei vecchi dispositivi si può lo stesso far eseguire senza crash la nostra app.

Permettetemi di dire la cosa in “tecnichese”: la modalità con cui si esegue il downgrade della user experience su device dotati di versioni di Android inferiori alla targetSdkVersion è un attività completamente demandata alla progettazione del software.

Un’altra osservazione da fare è che come detto più il divario tra minSdkVersion e targetSdkVersion è grande, maggiori saranno i punti in cui si dovranno mettere gli IF come quelli sopra. Ovviamente questo gap tra le due versioni aumenta solo se si vuole dare la possibilità, alla nostra app, di usare le nuove API messe a disposzione da Android.

Rimane sempre da analizzare l’altra parte del problema: istruzioni eliminate in versioni recenti, o di cui si è modificato il comportamento & nuovi template, ma li vedremo alla prossima putantata.

Linkografia

Meet Google Play’s target API level requirement

Getting Android Apps Ready for Google Play’s Target API Level Requirements

Mastering the Android Support Libraries

Such Android API levels, much confuse. Wow.

Xamarin Android: Il grande mistero degli Android API Level – Seconda e ultima parte