Parte 4 - Gestione di più piattaforme
Gestione delle differenze e delle caratteristiche della piattaforma
La divergenza non è solo un problema "multipiattaforma". i dispositivi nella stessa piattaforma hanno funzionalità diverse (in particolare l'ampia gamma di dispositivi Android disponibili). La più ovvia e di base è la dimensione dello schermo, ma altri attributi del dispositivo possono variare e richiedere a un'applicazione di verificare la presenza di determinate funzionalità e comportarsi in modo diverso in base alla loro presenza (o assenza).
Ciò significa che tutte le applicazioni devono gestire una normale riduzione delle funzionalità oppure presentare un set di funzionalità non attraente e comune-denominatore. L'integrazione approfondita di Xamarin con gli SDK nativi di ogni piattaforma consente alle applicazioni di sfruttare le funzionalità specifiche della piattaforma, quindi è opportuno progettare le app per usare tali funzionalità.
Per una panoramica delle differenze tra le piattaforme, vedere la documentazione sulle funzionalità della piattaforma.
Esempi di divergenza della piattaforma
Elementi fondamentali presenti su più piattaforme
Esistono alcune caratteristiche delle applicazioni per dispositivi mobili universali. Questi sono concetti di livello superiore che sono in genere veri di tutti i dispositivi e possono quindi costituire la base della progettazione dell'applicazione:
- Selezione delle funzionalità tramite schede o menu
- Elenchi di dati e scorrimento
- Visualizzazioni singole dei dati
- Modifica di singole visualizzazioni di dati
- Spostamento indietro
Quando si progetta il flusso dello schermo di alto livello, è possibile basare un'esperienza utente comune su questi concetti.
Attributi specifici della piattaforma
Oltre agli elementi di base presenti in tutte le piattaforme, è necessario risolvere le principali differenze della piattaforma nella progettazione. Potrebbe essere necessario prendere in considerazione (e scrivere codice in modo specifico per gestire) queste differenze:
- Dimensioni dello schermo: alcune piattaforme (ad esempio iOS e le versioni precedenti di Windows Telefono) hanno dimensioni dello schermo standardizzate relativamente semplici da usare come destinazione. I dispositivi Android hanno un'ampia gamma di dimensioni dello schermo, che richiedono maggiore impegno per supportare l'applicazione.
- Metafore di navigazione: differiscono tra le piattaforme (ad esempio il pulsante "Indietro" hardware, il controllo dell'interfaccia utente panorama) e nelle piattaforme (Android 2 e 4, i Telefono vs iPad).
- Tastiere: alcuni dispositivi Android hanno tastiere fisiche, mentre altre hanno solo una tastiera software. Il codice che rileva quando una tastiera soft nasconde parte dello schermo deve essere sensibile a queste differenze.
- Tocco e movimenti : il supporto del sistema operativo per il riconoscimento dei movimenti varia, soprattutto nelle versioni precedenti di ogni sistema operativo. Le versioni precedenti di Android hanno un supporto molto limitato per le operazioni di tocco, il che significa che il supporto di dispositivi meno recenti può richiedere codice separato
- Notifiche push: in ogni piattaforma sono disponibili funzionalità/implementazioni diverse, ad esempio Riquadri animati in Windows).
Funzionalità specifiche del dispositivo
Determinare le funzionalità minime necessarie per l'applicazione; o quando decidere quali funzionalità aggiuntive sfruttare in ogni piattaforma. Il codice sarà necessario per rilevare le funzionalità e disabilitare le funzionalità o offrire alternative (ad esempio, un'alternativa alla posizione geografica potrebbe essere quella di consentire all'utente di digitare una posizione o scegliere una mappa):
- Fotocamera : la funzionalità differisce tra i dispositivi: alcuni dispositivi non hanno una fotocamera, altri hanno fotocamere anteriori e posteriori. Alcune telecamere sono in grado di registrare video.
- Geo-location & maps : il supporto per la posizione GPS o Wi-Fi non è presente in tutti i dispositivi. Le app devono anche soddisfare i diversi livelli di accuratezza supportati da ogni metodo.
- Accelerometro, giroscopio e bussola : queste funzionalità sono spesso disponibili solo in una selezione di dispositivi in ogni piattaforma, quindi le app quasi sempre devono fornire un fallback quando l'hardware non è supportato.
- Twitter e Facebook : solo "incorporati" rispettivamente in iOS5 e iOS6. Nelle versioni precedenti e in altre piattaforme è necessario fornire funzioni di autenticazione personalizzate e interfaccia direttamente con l'API di ogni servizio.
- Near Field Communications (NFC): solo su (alcuni) telefoni Android (al momento della scrittura).
Gestione della divergenza della piattaforma
Esistono due approcci diversi per supportare più piattaforme dalla stessa code-base, ognuna con un proprio set di vantaggi e svantaggi.
- Astrazione della piattaforma : modello di facciata aziendale, fornisce un accesso unificato tra piattaforme e astrae le specifiche implementazioni della piattaforma in un'unica API unificata.
- Implementazione divergente: chiamata di funzionalità specifiche della piattaforma tramite implementazioni divergenti tramite strumenti architetturali come interfacce e ereditarietà o compilazione condizionale.
Astrazione della piattaforma
Astrazione della classe
Uso di interfacce o classi di base definite nel codice condiviso e implementate o estese in progetti specifici della piattaforma. La scrittura e l'estensione di codice condiviso con astrazioni di classi sono particolarmente adatte alle librerie di classi portabili perché dispongono di un subset limitato del framework a loro disposizione e non possono contenere direttive del compilatore per supportare rami di codice specifici della piattaforma.
Interfacce
L'uso delle interfacce consente di implementare classi specifiche della piattaforma che possono comunque essere passate nelle librerie condivise per sfruttare il codice comune.
L'interfaccia viene definita nel codice condiviso e passata alla libreria condivisa come parametro o proprietà.
Le applicazioni specifiche della piattaforma possono quindi implementare l'interfaccia e sfruttare comunque il codice condiviso per "elaborarlo".
Vantaggi
L'implementazione può contenere codice specifico della piattaforma e persino librerie esterne specifiche della piattaforma di riferimento.
Svantaggi
La necessità di creare e passare implementazioni nel codice condiviso. Se l'interfaccia viene usata in profondità all'interno del codice condiviso, finisce per essere passata attraverso più parametri del metodo o in caso contrario viene eseguito il push nella catena di chiamate. Se il codice condiviso usa molte interfacce diverse, devono essere tutte create e impostate nel codice condiviso da qualche parte.
Ereditarietà
Il codice condiviso può implementare classi astratte o virtuali che possono essere estese in uno o più progetti specifici della piattaforma. Questo comportamento è simile all'uso delle interfacce, ma con un comportamento già implementato. Esistono diversi punti di vista sul fatto che le interfacce o l'ereditarietà siano una scelta di progettazione migliore: in particolare perché C# consente solo l'ereditarietà singola, può determinare il modo in cui le API possono essere progettate in futuro. Usare l'ereditarietà con cautela.
I vantaggi e gli svantaggi delle interfacce si applicano ugualmente all'ereditarietà, con il vantaggio aggiuntivo che la classe di base può contenere codice di implementazione (ad esempio un'intera implementazione indipendente dalla piattaforma che può essere estesa facoltativamente).
Xamarin.Forms
Vedere la documentazione di Xamarin.Forms .
Altre librerie multipiattaforma
Queste librerie offrono anche funzionalità multipiattaforma per gli sviluppatori C#:
- Xamarin.Essentials : API multipiattaforma per le funzionalità comuni.
- SkiaSharp : grafica 2D multipiattaforma.
Compilazione condizionale
Esistono alcune situazioni in cui il codice condiviso dovrà comunque funzionare in modo diverso in ogni piattaforma, possibilmente accedendo a classi o funzionalità che si comportano in modo diverso. La compilazione condizionale funziona meglio con progetti di asset condivisi, in cui viene fatto riferimento allo stesso file di origine in più progetti con simboli diversi definiti.
I progetti Xamarin definiscono __MOBILE__
sempre che è true sia per i progetti di applicazioni iOS che per Android (si noti il doppio carattere di sottolineatura pre-correzione e post-correzione su questi simboli).
#if __MOBILE__
// Xamarin iOS or Android-specific code
#endif
iOS
Xamarin.iOS definisce __IOS__
quali è possibile usare per rilevare i dispositivi iOS.
#if __IOS__
// iOS-specific code
#endif
Ci sono anche simboli specifici per la tv e l'orologio:
#if __TVOS__
// tv-specific stuff
#endif
#if __WATCHOS__
// watch-specific stuff
#endif
Android
Il codice che deve essere compilato solo nelle applicazioni Xamarin.Android può usare quanto segue
#if __ANDROID__
// Android-specific code
#endif
Ogni versione dell'API definisce anche una nuova direttiva del compilatore, quindi il codice simile a questo consente di aggiungere funzionalità se sono destinate le API più recenti. Ogni livello API include tutti i simboli di livello "inferiore". Questa funzionalità non è molto utile per supportare più piattaforme; in genere il __ANDROID__
simbolo sarà sufficiente.
#if __ANDROID_11__
// code that should only run on Android 3.0 Honeycomb or newer
#endif
Mac
Xamarin.Mac definisce __MACOS__
quale è possibile usare per la compilazione solo per macOS:
#if __MACOS__
// macOS-specific code
#endif
Piattaforma UWP (Universal Windows Platform)
Usare WINDOWS_UWP
. Non sono presenti caratteri di sottolineatura che circondano la stringa come i simboli della piattaforma Xamarin.
#if WINDOWS_UWP
// UWP-specific code
#endif
Uso della compilazione condizionale
Un semplice esempio di case study di compilazione condizionale consiste nell'impostare il percorso del file di database SQLite. Le tre piattaforme hanno requisiti leggermente diversi per specificare il percorso del file:
- iOS : Apple preferisce che i dati non utente vengano inseriti in una posizione specifica (la directory Library), ma non esiste alcuna costante di sistema per questa directory. Il codice specifico della piattaforma è necessario per compilare il percorso corretto.
- Android : il percorso di sistema restituito da
Environment.SpecialFolder.Personal
è un percorso accettabile per archiviare il file di database. - Windows Telefono: il meccanismo di archiviazione isolata non consente di specificare un percorso completo, solo un percorso relativo e un nome file.
- piattaforma UWP (Universal Windows Platform): usa le
Windows.Storage
API.
Il codice seguente usa la compilazione condizionale per assicurarsi che sia DatabaseFilePath
corretto per ogni piattaforma:
public static string DatabaseFilePath
{
get
{
var filename = "TodoDatabase.db3";
#if SILVERLIGHT
// Windows Phone 8
var path = filename;
#else
#if __ANDROID__
string libraryPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
#else
#if __IOS__
// we need to put in /Library/ on iOS5.1 to meet Apple's iCloud terms
// (they don't want non-user-generated data in Documents)
string documentsPath = Environment.GetFolderPath (Environment.SpecialFolder.Personal); // Documents folder
string libraryPath = Path.Combine (documentsPath, "..", "Library");
#else
// UWP
string libraryPath = Windows.Storage.ApplicationData.Current.LocalFolder.Path;
#endif
#endif
var path = Path.Combine(libraryPath, filename);
#endif
return path;
}
}
Il risultato è una classe che può essere compilata e usata in tutte le piattaforme, inserendo il file di database SQLite in un percorso diverso in ogni piattaforma.