Å skrive kode en gang og bruke den på flere plattformer har vært en drøm for mange programvareutviklere. Selv om dette har vært mulig en stund nå, setter det alltid vedlikehold, enkel testing, eller verre, en dårlig brukeropplevelse i fare.
Å utvikle mobilapplikasjoner ved hjelp av den innebygde SDK er sannsynligvis utgangspunktet for mange utviklere som har sine røtter i utviklingen av stasjonære applikasjoner. Programmeringsspråk vil være en barriere for noen: Hvis noen hadde erfaring med å utvikle Java desktop- eller back-end-applikasjoner, kan det være lettere å flytte til Android enn å starte Objective-C fra bunnen av for iOS.
Jeg var alltid i tvil med applikasjonsutvikling på tvers av plattformer. De rammer basert på JavaScript som Sencha, Cordova, Titanium, etc. de har aldri vært gode valg når ytelse betyr noe. Mangelen på API og en bestemt brukeropplevelse var nøkkelen med disse rammer .
Men jeg oppdaget Xamarin.
I denne artikkelen vil du lære hvordan du kan bruke Xamarin til å dele kode på tvers av flere plattformer uten å gå på kompromiss med noe annet ved utvikling av mobilapper. Artikkelen vil spesielt fokusere på Android og iOS, men du kan bruke andre plattformer som Xamarin støtter.
Xamarin er en utviklingsplattform som lar deg skrive på tvers av plattformer - om enn innfødte - applikasjoner for iOS, Android og Windows Phone i C # og .NET.
Xamarin gir bindinger C # til Android og iOS native APIer. Dette gir deg muligheten til å bruke alt det opprinnelige brukergrensesnittet, varsler, grafikk, animasjon og andre telefonfunksjoner - og de bruker alle C #.
Xamarin når alle nye Android- og iOS-utgivelser, med en utgivelse som inkluderer bindinger for de nye API-ene dine.
Xamarin .NET-porten inneholder funksjoner som datatyper, generisk, gjenvinning av papirkurven, språkintegrert spørring (LINQ), asynkront programmeringsmønster, delegering og en delmengde av Windows Communication Foundation (WCF). Biblioteker administreres med en somle å bare inkludere de henviste komponentene.
Xamarin.Forms er et lag som sitter på toppen av de andre bindinger UI og Windows Phone API, som gir et fullt plattformbasert brukergrensesnittbibliotek.
For å kunne skrive plattformapplikasjoner med Xamarin, må utviklere velge en av de to tilgjengelige prosjekttypene:
PCL lar deg skrive kode som kan deles mellom flere plattformer, men med en begrensning. Siden ikke alle .NET APIer er tilgjengelige på alle plattformer, med et PLC-prosjekt, vil du begrense det til å bare utføres på plattformer det er beregnet for.
Tabellen nedenfor viser hvilke API-er som er tilgjengelige på hvilke plattformer:
Trekk | .NET Framework | Windows Store-apper | Silverlight | Windows telefon | Xamarin |
---|---|---|---|---|---|
Kjerne | Y | Y | Y | Y | Y |
LINQ | Y | Y | Y | Y | Y |
IQueryable | Y | Y | Y | 7.5+ | Y |
Serialisering | Y | Y | Y | Y | Y |
Dataanmerkninger | 4.0.3+ | Y | Y | Y | Y |
Under byggeprosessen blir en PCL samlet i forskjellige DLLer og lastet av Mono under henrettelse. En annen implementering, men med samme grensesnitt, kan gis under utførelse.
På den annen side gir delte prosjekter mer kontroll, fordi de lar deg skrive plattformspesifikk kode for hver plattform du vil støtte. Koden i et delt prosjekt kan inneholde direktører for datakonvertering som vil slå kodedeler av eller på, avhengig av hvilket applikasjonsprosjekt som bruker koden.
I motsetning til en PCL produserer ikke et delt prosjekt noen DLL-filer. Koden er inkludert direkte i sluttprosjektet.
Gjenbrukbar kode kan bidra til å spare utviklingsteam penger og tid. Velstrukturert kode gjør imidlertid livet lettere for utviklere. Ingen setter pris på velskrevet kode uten bugs som utviklere.
Xamarin gir bare en mekanisme som gjør det enklere å skrive gjenbrukbar kode på tvers av plattformer.
Mobilutviklere er kjent med scenarier der de må skrive den samme logikken to ganger eller mer for å støtte iOS, Android og andre plattformer. Men med Xamarin, som jeg forklarte i forrige kapittel, er det enkelt å gjenbruke kode på andre plattformer, selv om den ble skrevet for en bestemt plattform.
Så på hvilket tidspunkt kommer MvvmCross inn?
MvvmCross, som navnet antyder, gjør det mulig å bruke MVVM-mønsteret i Xamarin-applikasjoner. Den leveres med forskjellige biblioteker, API-er og tjenester som er veldig viktige i plattformsapplikasjonsutvikling.
MvvmCross kan redusere mengden kode betydelig kokeplate hva du ville ha skrevet (noen ganger gjentatte ganger på forskjellige språk) i en hvilken som helst annen tilnærming til applikasjonsutvikling.
MvvmCross-samfunnet anbefaler en veldig enkel og effektiv måte å strukturere en MvvmCross-løsning på:
.Core .UI.Droid .UI.iOS
Prosjektet Kjerne i en løsning er MvvmCross relatert til gjenbrukbar kode. Core-prosjektet er et Xamarin PCL-prosjekt, hvor hovedfokuset er gjenbrukbarhet.
Eventuell kode skrevet inn Kjerne Det bør være, der det er mulig, agnostisk plattform. Den skal bare inneholde logikk, som kan brukes på alle plattformer. Prosjektet Kjerne Du bør ikke bruke Android- eller iOS-API-ene, eller angi noe spesifikt for noen plattform.
Virksomhetslogikklaget, datalaget og kommunikasjonen baksiden , er perfekte kandidater for å bli inkludert i prosjektet Kjerne . Navigering gjennom hierarkivisningen (aktiviteter, fragmenter, etc.) vil bli oppnådd i kjernen.
Før du fortsetter, er det nødvendig å forstå et arkitektonisk designmønster, noe som er avgjørende for å forstå mer om MvvmCross og hvordan det fungerer. Som det fremgår av navnet, er MvvmCross sterkt avhengig av MVVM-mønsteret.
MVVM er et arkitektonisk designmønster som gjør det mulig å skille det grafiske brukergrensesnittet fra forretningslogikken og data-backend.
Hvordan brukes dette mønsteret i MvvmCross?
Vel, siden vi ønsker å oppnå et høyt nivå på gjenbrukbarhet av koden vår, vil vi ha så mye som mulig i vår Kjerne , som er et PLC-prosjekt. Siden visninger er den eneste delen av koden som er forskjellig fra plattform til plattform, kan vi ikke bruke dem på tvers av plattformer. Denne delen er implementert i prosjekter knyttet til plattformen.
MvvmCross gir oss muligheten til å lage en applikasjonsnavigasjon fra Kjerne ved hjelp av ViewModels.
Med det grunnleggende og tekniske detaljene på plass, la oss komme i gang med Xamarin ved å lage vårt eget Core MvvmCross-prosjekt:
Åpne Xamarin Studio og lag en løsning som heter ApeeScapeExampleSolution
:
Siden vi lager et prosjekt Kjerne , er det lurt å holde seg til navnekonvensjonen. Forsikre deg om at suffikset Core
legges til prosjektnavnet.
For å få MvvmCross-støtte, er det nødvendig å legge til MvvmCross-biblioteker i prosjektet vårt. For å legge til dette kan vi bruke inkludert støtte for NuGet i Xamarin Studio.
For å legge til et bibliotek, høyreklikk på mappen Pakker og velg alternativet Legg til pakker ... .
I søkefeltet kan vi søke etter MvvmCross, som vil fungere som et filter og finne resultater relatert til MvvmCross som vist nedenfor:
Ved å klikke på knappen Legg til pakke vil bli lagt til prosjektet.
Med MvvmCross lagt til prosjektet, er vi klare til å skrive koden vår Kjerne .
La oss definere vårt første ViewModel . For å lage en, opprett et mappehierarki som følger:
Her ser du hva hver mappe handler om:
Vår første ViewModel heter FirstViewModel.cs
public class FirstViewModel : MvxViewModel { private string _firstName; private string _lastName; private string _fullName; public string FirstName { get { return _firstName; } set { _lastName = value; RaisePropertyChanged(); } } public string LastName { get { return _lastName; } set { _lastName = value; RaisePropertyChanged(); } } public string FullName { get { return _fullName; } set { _fullName = value; RaisePropertyChanged(); } } public IMvxCommand ConcatNameCommand { get { return new MvxCommand(() => { FullName = $'{FirstName} {LastName}'; }); } public IMvxCommand NavigateToSecondViewModelCommand { get { return new MvxCommand(() => { ShowViewModel(); }); } } }
Nå som vi har vår første ViewModel , kan vi lage vårt første syn og sette alt sammen.
For å vise innholdet i ViewModel , må vi opprette et brukergrensesnitt.
Det første trinnet i å lage et Android-brukergrensesnitt er å lage et Android-prosjekt i den nåværende løsningen. For å gjøre dette, høyreklikk på navnet på løsningen og velg Legg til -> Legg til nytt prosjekt ... . På veiviseren , velg Android-app og sørg for å gi prosjektet et navn ApeeScapeExample.UI.Droid
.
Som jeg har beskrevet tidligere, må vi nå legge til MvvmCross-avhengigheter for Android. For å gjøre det, følg de samme trinnene i prosjektet Kjerne for å legge til NuGet-avhengigheter.
Etter å ha lagt til MvvmCross-avhengighetene, må du legge til en referanse for prosjektet vårt Kjerne slik at vi kan bruke koden vår skrevet der. For å legge til en referanse for PCL-prosjektet, høyreklikk på Referanser-mappen og velg alternativet Rediger referanser ... . Velg prosjektet i kategorien Prosjekter Kjerne tidligere opprettet, og klikk OK.
Den neste delen kan være litt vanskelig å forstå.
Nå må vi fortelle MvvmCross hvordan vi installerer applikasjonen vår. For å gjøre dette må vi opprette klassen Setup
:
namespace ApeeScapeExample.UI.Droid { public class Setup : MvxAndroidSetup { public Setup(Context context) : base(context) { } protected override IMvxApplication CreateApp() { return new Core.App(); } } }
Som du kan se i klassen, ber vi MvvmCross om å lage en base CreateApp
i implementeringen Core.App
, som er en klasse definert i Kjerne og er vist nedenfor:
public class App : MvxApplication { public override void Initialize() { RegisterAppStart(new AppStart()); } } public class AppStart : MvxNavigatingObject, IMvxAppStart { public void Start(object hint = null) { ShowViewModel(); } }
I klassen App
lager vi en forekomst av AppStart
, som skal vise vår første ViewModel .
Det eneste som er igjen å gjøre nå er å lage en Android-layoutfil, som vil bli fulgt av MvvmCross:
EditText
I designfilen har vi bindinger som løses automatisk av MvvmCross. For View
, la oss lage en bindende for Tekst-egenskapen, som vil være en bindende dobbeltspor. Eventuelle endringer påkalt fra ViewModel-siden reflekteres automatisk i visningen og omvendt.
Klassen [Activity(Label = 'ApeeScapeExample.UI.Droid', Theme = '@style/Theme.AppCompat', MainLauncher = true, Icon = '@mipmap/icon')] public class MainActivity : MvxAppCompatActivity { protected override void OnCreate(Bundle bundle) { base.OnCreate(bundle); SetContentView(Resource.Layout.Main); } }
det kan være en aktivitet eller et fragment. For enkelhets skyld bruker vi en aktivitet som laster den gitte layouten:
ContactNameCommand
For den første knappen har vi en kommando bindende , som betyr at når du klikker på MvvmCross-knappen, vil den påkalle Setup
fra ViewModel.
For den andre knappen skal vi vise en annen ViewModel.
Å lage et iOS-prosjekt er ikke veldig forskjellig fra å lage et Android-prosjekt. Du må følge lignende trinn for å legge til et nytt prosjekt, bare denne gangen, i stedet for Android, oppretter du bare et iOS-prosjekt. Bare sørg for å holde navnekonvensjonen aktiv.
Etter at du har lagt til iOS-prosjektet, må du legge til avhengigheter for MvvmCross iOS. Trinnene er nøyaktig de samme som for Kjerne og Android (høyreklikk Referanser i iOS-prosjektet ditt og klikk Legg til referanser ... ).
Nå, som vi gjorde med Android, er det nødvendig å opprette en klasse public class Setup : MvxIosSetup { public Setup(MvxApplicationDelegate appDelegate, IMvxIosViewPresenter presenter) : base(appDelegate, presenter) { } protected override MvvmCross.Core.ViewModels.IMvxApplication CreateApp() { return new App(); } }
, som vil fortelle MvvmCross hvordan vi installerer applikasjonen vår.
Setup
Merk at klassen AppDelegate
nå utvide MvxIosSetup og for Android utvidet den seg MvxAndroidSetup .
Et tillegg her er at vi må endre klasse AppDelegate
.
[Register('AppDelegate')] public class AppDelegate : MvxApplicationDelegate { // class-level declarations public override UIWindow Window { get; set; } public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions) { Window = new UIWindow(UIScreen.MainScreen.Bounds); var presenter = new MvxIosViewPresenter(this, Window); var setup = new Setup(this, presenter); setup.Initialize(); var startup = Mvx.Resolve(); startup.Start(); Window.MakeKeyAndVisible(); return true; } }
på iOS er det ansvarlig for å starte brukergrensesnittet, så vi må se hvordan visningene blir gjengitt på iOS. Du kan lære mer om programlederne her .
NavigateToSecondViewModelCommand
For å presentere VIewModel, må vi lage en visning. For det spesielle tilfellet skal vi lage en ViewController, høyreklikk på prosjektet og velg Legg til -> Ny fil og velg ViewController i iOS-delen, som vi skal gi navnet FirstViewController til.
Xamarin oppretter tre filer, der vi skal definere som våre bindinger . I motsetning til Android, for iOS, må vi definere vår bindinger på en annen måte, gjennom kode (selv om vi også kan gjøre det i Android, og i noen tilfeller er det nødvendig å gjøre det).
Når det er nødvendig å navigere mellom visningene, gjøres det via ViewModel. I kommandoen ShowViewModel()
, er metoden MvxAppCompatActivity
den finner riktig visning og navigerer gjennom den.
Men hvordan vet MVVMCross hvilken visning du skal laste inn?
Det er ikke noe magisk ved det. Når vi lager en visning for Android (Aktivitet eller Fragment) utvider vi en av basisklassene med typeparametere (ShowViewMolel
). Når vi ringer View
, ser MvvmCross etter en Activity
(Vis) som utvider klassen Fragment
eller VM
med parametere av typen public interface IPasswordGeneratorService { string Generate(int length); }
. Dette er grunnen til at du ikke kan ha to visningsklasser for samme ViewModel.
Xamarin leverer bare C # -omslag rundt de opprinnelige APIene, og gir ikke noen form for avhengighetsinjeksjon (DI) eller inversjon av kontroll (loC) -mekanismen.
Uten en avhengighetsinnsprøytning eller kompileringstidsinjeksjon er det ikke lett å lage løst sammenføyde, gjenbrukbare, testbare og vedlikeholdbare komponenter. Ideen om loC og DI har vært kjent i lang tid; detaljer om loC finner du i mange artikler på nettet. Du kan lære mer om disse mønstrene fra innledende artikkel av Martin Fowler .
IoC har vært tilgjengelig siden de første versjonene av MvvmCrosses og tillater avhengighetsinjeksjon ved kjøretid, når applikasjonen starter og når det er nødvendig.
For å få løst koblede komponenter, bør vi aldri kreve konkrete klasseimplementeringer. Å kreve spesifikke implementeringer begrenser muligheten til å endre atferden til implementeringene ved kjøretid (du kan ikke erstatte den med en annen implementering). Dette gjør det vanskelig å teste disse komponentene.
Av denne grunn skal vi erklære et grensesnitt som vi skal ha en konkret implementering for.
public class PasswordGeneratorService : IPasswordGeneratorService { public string Generate(int length) { var valid = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'; var res = new StringBuilder(); var rnd = new Random(); while (0 Og implementering:
IPasswordGenerationService
Nå kan ViewModel kreve en forekomst av grensesnittet PasswordGeneratorService
, som det er vårt ansvar å gi.
For at MvvmCross skal injisere implementeringen App.cs
ved kjøretid, må vi fortelle MvvmCross hvilken implementering vi skal bruke. Hvis vi vil bruke en implementering for begge plattformene, kan vi registrere implementeringen i public override void Initialize() { RegisterAppStart(new AppStart()); Mvx.LazyConstructAndRegisterSingleton(); }
, etter applikasjonsregistreringen:
LazyConstructAndRegisterSingleton
Ovennevnte kall til den statiske metoden Mvx.RegisterSingleton()
registrerer implementeringen som skal injiseres. Denne metoden registrerer riktig implementering, men skaper ikke et objekt.
Varen opprettes bare når det er nødvendig, og bare en gang, da det er registrert som en ensom.
Hvis vi ønsker å lage et ensomt objekt med en gang, kan det gjøres ved å ringe Mvx.RegisterType()
.
Det er tilfeller der vi ikke vil ha bare advokater i søknaden vår. Kanskje vårt objekt ikke er feilsikkert, eller kanskje det er en annen grunn til at vi alltid vil ha en ny forekomst. Hvis det er tilfelle, gir MvvmCross metoden public class DroidPasswodGeneratorService : IPasswordGeneratorService { public string Generate(int length) { return 'DroidPasswordGenerator'; } }
, som kan brukes til å registrere implementeringen på en måte som instantierer en ny forekomst der det er nødvendig.
I tilfelle du trenger å gi spesifikke implementeringer for hver plattform, kan du gjøre det i de spesifikke plattformprosjektene:
Setup.cs
Og registreringen av implementeringen vår gjøres i klassen protected override void InitializePlatformServices() { base.InitializePlatformServices(); Mvx.LazyConstructAndRegisterSingleton(); }
under Droid-prosjektet:
InitializePlatformServices
Etter å ha startet PCL-koden vil MvvmCross ringe
|_+_|
og vil registrere våre plattformspesifikke tjenesteimplementeringer. Når vi registrerer flere ensomme implementeringer, vil MvvmCross bare bruke den sist registrerte implementeringen. Alle andre registreringer vil bli avvist.
Bygg applikasjoner på tvers av plattformer med Xamarin
I denne artikkelen har vi sett hvordan Xamarin lar deg dele kode mellom forskjellige plattformer og opprettholde den opprinnelige følelsen og ytelsen til applikasjonene.
MvvmCross gir et nytt lag med abstraksjon, og utvider opplevelsen av å bygge applikasjoner på tvers av plattformer med Xamarin. MVVM-mønsteret gir en måte å skape navigasjons- og brukerinteraksjonsflyter, som er felles for alle plattformer, og dermed blir mengden plattformspesifikk kode du trenger å skrive begrenset til visninger.
Jeg håper denne artikkelen ga deg en grunn til å ta en titt på Xamarin, og motiverte deg også til å bygge din neste plattformsapp med den.