Crearea de aplicații pe mai multe platforme cu Xamarin: Perspectiva unui dezvoltator Android

Publicat: 2022-03-11

A scrie cod o dată și a-l folosi pe mai multe platforme a fost un vis al multor dezvoltatori de software. Deși acest lucru este posibil de ceva timp încoace, a venit întotdeauna cu prețul menținabilității, ușurinței de testare sau chiar mai rău, a unei experiențe slabe de utilizator.

Dezvoltarea de aplicații mobile folosind SDK-ul nativ este probabil punctul de plecare pentru toți dezvoltatorii care își au rădăcinile în domeniul dezvoltării aplicațiilor desktop. Limbajele de programare ar deveni o barieră pentru unii: dacă cineva ar avea experiență în dezvoltarea de aplicații Java desktop sau back-end, mutarea la o firmă de dezvoltare de aplicații mobile și lucrul cu Android s-ar simți mult mai ușor decât a începe cu Objective-C de la zero pentru iOS.

Am fost întotdeauna sceptic cu privire la dezvoltarea de aplicații multiplatforme. Frame-urile bazate pe JavaScript precum Sencha, Cordova, Titanium etc. nu se dovedesc niciodată a fi o alegere înțeleaptă atunci când performanța este importantă. Lipsa API-urilor și experiența de utilizator ciudată au fost date cu aceste cadre.

Dar apoi, am dat peste Xamarin.

Dezvoltare multiplatformă cu Xamarin

În acest articol, veți afla cum puteți utiliza Xamarin pentru a partaja cod pe mai multe platforme fără a compromite niciunul dintre celelalte aspecte ale dezvoltării aplicațiilor mobile. Articolul se va concentra pe Android și iOS în special, dar puteți utiliza o abordare similară, adăugați suport pentru orice altă platformă pe care o acceptă Xamarin.

Ce este Xamarin?

Xamarin este o platformă de dezvoltare care vă permite să scrieți aplicații multiplatformă, dar native, pentru iOS, Android și Windows Phone în C# și .NET.

Xamarin oferă legături C# la API-urile native Android și iOS. Acest lucru vă oferă puterea de a utiliza toate interfețele de utilizator native Android și iOS, notificările, grafica, animația și alte funcții ale telefonului, toate folosind C#.

Fiecare nouă lansare de Android și iOS este egalată de Xamarin, cu o nouă versiune care include legături pentru noile lor API-uri.

Portul .NET al lui Xamarin include caracteristici precum tipuri de date, generice, colectare de gunoi, interogare integrată în limbaj (LINQ), modele de programare asincronă, delegați și un subset al Windows Communication Foundation (WCF). Bibliotecile sunt gestionate cu un linger pentru a include doar componentele la care se face referire.

Xamarin.Forms este un strat deasupra celorlalte legături UI și API-ul Windows Phone, care oferă o bibliotecă de interfață utilizator complet multiplatformă.

Domeniul de aplicare al Xamarin

Scrierea de aplicații multiplatforme

Pentru a scrie aplicații multiplatformă cu Xamarin, dezvoltatorii trebuie să aleagă unul dintre cele două tipuri de proiecte disponibile:

  • Bibliotecă portabilă de clasă (PCL)
  • Proiect comun

PCL vă permite să scrieți cod care poate fi partajat între mai multe platforme, dar cu o singură limitare. Deoarece nu toate API-urile .NET sunt disponibile pe toate platformele, cu un proiect PCL, îl veți limita să ruleze pe platformele pentru care este vizat.

Conexiunile și limitările lui Xamarin

Tabelul de mai jos arată ce API-uri sunt disponibile pe ce platforme:

Caracteristică .Cadru net Aplicații Windows Store Silverlight Windows Phone Xamarin
Miez Y Y Y Y Y
LINQ Y Y Y Y Y
IQueryable Y Y Y 7,5+ Y
Serializare Y Y Y Y Y
Adnotări de date 4.0.3+ Y Y Y Y

În timpul procesului de construire, un PCL este compilat în DLL-uri separate și încărcat de Mono în timpul rulării. O implementare diferită a aceleiași interfețe poate fi furnizată în timpul rulării.

Pe de altă parte, proiectele partajate vă oferă mai mult control, permițându-vă să scrieți cod specific platformei pentru fiecare platformă pe care doriți să o susțineți. Codul dintr-un proiect partajat poate conține directive de compilator care vor activa sau dezactiva secțiuni de cod, în funcție de proiectul aplicației care folosește codul.

Spre deosebire de un PCL, un proiect partajat nu produce niciun DLL. Codul este inclus direct în proiectul final.

Oferiți structură codului dvs. multiplatform cu MvvmCross

Codul reutilizabil poate economisi bani și timp pentru echipele de dezvoltare. Cu toate acestea, codul bine structurat face viața mult mai ușoară dezvoltatorilor. Nimeni nu apreciază mai mult codul frumos scris fără erori decât dezvoltatorii.

Xamarin în sine oferă un mecanism care face mult mai ușoară scrierea codului reutilizabil pe mai multe platforme.

Dezvoltatorii de telefonie mobilă sunt familiarizați cu scenariile în care trebuie să scrie aceeași logică de două ori sau mai multe pentru a suporta iOS, Android și alte platforme. Dar cu Xamarin, așa cum sa explicat în capitolul anterior, este ușor să reutilizați codul care este scris pentru o platformă și pentru alte platforme.

Atunci, unde intervine MvvmCross?

MvvmCross, după cum poate sugera și numele, face posibilă utilizarea modelului MVVM în aplicațiile Xamarin. Vine cu o mulțime de biblioteci, API-uri și utilități care sunt cu adevărat utile în dezvoltarea de aplicații multiplatformă.

MvvmCross poate reduce semnificativ cantitatea de cod standard pe care l-ați fi scris (uneori de mai multe ori în diferite limbi) în orice altă abordare a dezvoltării aplicațiilor.

Structura unei soluții MvvmCross

Comunitatea MvvmCross recomandă o modalitate destul de simplă și eficientă de structurare a unei soluții MvvmCross:

 <ProjectName>.Core <ProjectName>.UI.Droid <ProjectName>.UI.iOS

Proiectul Core dintr-o soluție MvvmCross este legat de codul reutilizabil. Proiectul Core este un proiect Xamarin PCL în care principalul obiectiv este reutilizarea.

Orice cod scris în Core ar trebui să fie independent de platformă în modul maxim posibil. Ar trebui să conțină doar logică care poate fi reutilizată pe toate platformele. Proiectul Core nu trebuie să folosească niciun API Android sau iOS și nici să acceseze nimic specific oricărei platforme.

Stratul logic de afaceri, stratul de date și comunicarea back-end sunt toate candidații perfecti pentru a fi incluse în proiectul Core. Navigarea prin ierarhia vizualizărilor (activități, fragmente etc.) se va realiza în Core.

Înainte de a continua, este necesar să înțelegeți un model de design arhitectural, care este crucial pentru înțelegerea MvvmCross și a modului în care funcționează. După cum se poate vedea din nume, MvvmCross depinde în mare măsură de modelul MVVM.

MVVM este un model de design arhitectural care facilitează separarea interfeței grafice cu utilizatorul de logica de afaceri și de datele back-end.

Cum este utilizat acest model în MvvmCross?

Ei bine, din moment ce dorim să obținem o reutilizare ridicată a codului nostru, dorim să avem cât de mult putem în Core, care este un proiect PCL. Deoarece vizualizările sunt singura parte a codului care diferă de la o platformă la alta, nu le putem reutiliza pe platforme. Acea parte este implementată în proiectele legate de platformă.

Structura MvvmCross

MvvmCross ne oferă capacitatea de a orchestra navigarea aplicației din Core folosind ViewModels.

Cu elementele de bază și detaliile tehnice îndepărtate, să începem cu Xamarin prin crearea propriului nostru proiect MvvmCross Core:

Crearea unui proiect de bază MvvmCross

Deschideți Xamarin Studio și creați o soluție numită ToptalExampleSolution :

Crearea soluției

Deoarece creăm un proiect de bază, este o idee bună să rămânem cu convenția de denumire. Asigurați-vă că sufixul Core este adăugat la numele proiectului.

Pentru a obține suport MvvmCross, este necesar să adăugați biblioteci MvvmCross la proiectul nostru. Pentru a adăuga că putem folosi suport încorporat pentru NuGet în Xamarin Studio.

Pentru a adăuga o bibliotecă, faceți clic dreapta pe folderul Pachete și selectați opțiunea Adăugați pachete....

În câmpul de căutare, putem căuta MvvmCross, care va filtra rezultatele legate de MvvmCross, după cum se arată mai jos:

Filtrarea rezultatelor

Făcând clic pe butonul Adăugare pachet , îl va adăuga la proiect.

Cu MvvmCross adăugat la proiectul nostru, suntem gata să scriem codul nostru de bază.

Să definim primul nostru ViewModel. Pentru a crea unul, creați o ierarhie de foldere după cum urmează:

Ierarhie recomandată de foldere

Iată despre ce este vorba în fiecare dintre dosare:

  • Modele: modele de domenii care reprezintă conținut real
  • Servicii: un folder care deține serviciul nostru (logica de afaceri, baza de date etc.)
  • ViewModel: modul în care comunicăm cu modelele noastre

Primul nostru ViewModel se numește 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<SecondViewModel>(); }); } } }

Acum că avem primul nostru ViewModel, putem crea prima noastră vizualizare și putem lega lucrurile împreună.

Interfața de utilizare Android

Pentru a afișa conținutul ViewModel, trebuie să creăm o interfață de utilizare.

Primul pas pentru crearea unei interfețe de utilizare Android este crearea unui proiect Android în soluția actuală. Pentru a face asta, faceți clic dreapta pe numele soluției și selectați Adăugare -> Adăugare proiect nou... . În expert, selectați aplicația Android și asigurați-vă că denumiți proiectul ToptalExample.UI.Droid .

După cum s-a descris mai devreme, acum trebuie să adăugăm dependențe MvvmCross pentru Android. Pentru a face asta, urmați aceiași pași ca și pentru proiectul Core pentru adăugarea dependențelor NuGet.

După adăugarea dependențelor MvvmCross, este necesar să adăugați o referință la proiectul nostru Core, astfel încât să putem folosi codul scris acolo. Pentru a adăuga o referință la proiectul PCL, faceți clic dreapta pe folderul Referințe și selectați opțiunea Editare referințe.... În fila Proiecte, selectați proiectul Core creat anterior și faceți clic pe OK.

Adăugarea unei referințe la proiectul PCL

Următoarea parte poate fi puțin dificil de înțeles.

Acum trebuie să spunem lui MvvmCross cum ar trebui să configureze aplicația noastră. Pentru a face asta, trebuie să creăm o clasă Setup :

 namespace ToptalExample.UI.Droid { public class Setup : MvxAndroidSetup { public Setup(Context context) : base(context) { } protected override IMvxApplication CreateApp() { return new Core.App(); } } }

După cum se poate vedea din clasă, îi spunem lui MvvmCross să CreateApp pe baza implementării Core.App , care este o clasă definită în Core și prezentată mai jos:

 public class App : MvxApplication { public override void Initialize() { RegisterAppStart(new AppStart()); } } public class AppStart : MvxNavigatingObject, IMvxAppStart { public void Start(object hint = null) { ShowViewModel<FirstViewModel>(); } }

În clasa App , creăm o instanță a AppStart , care va arăta primul nostru ViewModel.

Singurul lucru rămas acum este să creați un fișier Android Layout care va fi legat de MvvmCross:

 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:andro xmlns:local="http://schemas.android.com/apk/res-auto" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <EditText android:layout_width="match_parent" android:layout_height="match_parent" local:MvxBind="Text FirstName" /> <EditText android:layout_width="match_parent" android:layout_height="match_parent" local:MvxBind="Text LastName" /> <TextView android:layout_width="match_parent" android:layout_height="match_parent" local:MvxBind="Text FullName" /> <Button android:layout_width="match_parent" android:layout_height="wrap_content" local:MvxBind="Click ConcatNameCommand" /> <Button android:layout_width="match_parent" android:layout_height="wrap_content" local:MvxBind="Click NavigateToSecondViewModelCommand" /> </LinearLayout>

În fișierul de aspect, avem legături care sunt rezolvate automat de MvvmCross. Pentru EditText , creăm o legare pentru proprietatea Text, care va fi o legare în două sensuri. Orice modificare invocată din partea ViewModel va fi reflectată automat pe vizualizare și invers.

Clasa View poate fi o activitate sau un fragment. Pentru simplitate, folosim o activitate care încarcă aspectul dat:

 [Activity(Label = "ToptalExample.UI.Droid", Theme = "@style/Theme.AppCompat", MainLauncher = true, Icon = "@mipmap/icon")] public class MainActivity : MvxAppCompatActivity<FirstViewModel> { protected override void OnCreate(Bundle bundle) { base.OnCreate(bundle); SetContentView(Resource.Layout.Main); } }

Pentru primul buton, avem o legare de comandă, ceea ce înseamnă că atunci când facem clic pe butonul MvvmCross va invoca ContactNameCommand din ViewModel.

Pentru al doilea buton, vom arăta un alt ViewModel.

UI iOS

Crearea unui proiect iOS nu este cu adevărat diferită de crearea unui proiect Android. Trebuie să urmați pași similari pentru adăugarea unui nou proiect, doar că de data aceasta, în loc de Android, trebuie doar să creați un proiect iOS. Doar asigurați-vă că păstrați convenția de denumire consecventă.

După adăugarea proiectului iOS, trebuie să adăugați dependențe pentru MvvmCross iOS. Pașii sunt absolut aceiași ca pentru Core și Android (dați clic dreapta pe Referințe în proiectul dvs. iOS și faceți clic pe Adăugare referințe... ).

Acum, așa cum am făcut pentru Android, este necesar să se creeze o clasă Setup , care îi va spune lui MvvmCross cum să configureze aplicația noastră.

 public class Setup : MvxIosSetup { public Setup(MvxApplicationDelegate appDelegate, IMvxIosViewPresenter presenter) : base(appDelegate, presenter) { } protected override MvvmCross.Core.ViewModels.IMvxApplication CreateApp() { return new App(); } }

Rețineți că clasa Setup extinde acum MvxIosSetup și, pentru Android, extinde MvxAndroidSetup .

O adăugare aici este că trebuie să ne schimbăm clasa AppDelegate .

AppDelegate pe iOS este responsabil pentru lansarea interfeței cu utilizatorul, așa că trebuie să spunem cum vor fi prezentate vizualizările pe iOS. Puteți afla mai multe despre prezentatori aici.

 [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<IMvxAppStart>(); startup.Start(); Window.MakeKeyAndVisible(); return true; } }

Pentru a prezenta modelul nostru de vizualizare, trebuie să creăm o vizualizare. În acest caz, să creăm un ViewController făcând clic dreapta pe proiect și selectând Add -> New File și selectând ViewController din secțiunea iOS, pe care o vom numi FirstViewController.

Xamarin creează trei fișiere în care vom defini care vor fi legăturile noastre. Spre deosebire de Android, pentru iOS, trebuie să ne definim legăturile într-un mod diferit, prin cod (deși putem face asta și pe Android și, în unele cazuri, este necesar să facem acest lucru).

Când este necesară navigarea între vizualizări, aceasta se face prin ViewModel. În comanda NavigateToSecondViewModelCommand , metoda ShowViewModel<SecondViewModel>() va găsi vizualizarea corespunzătoare și va naviga la aceasta.

Dar, de unde știe MVVMCross ce vizualizare să încarce?

Nu există magie în asta. Când creăm o vizualizare pentru Android (Activity sau Fragment) extindem una dintre clasele de bază cu parametri de tip ( MvxAppCompatActivity<VM> ). Când numim ShowViewMolel<VM> , MvvmCross caută o View care extinde clasa Activity sau Fragment cu parametrii de tip VM . Acesta este motivul pentru care nu aveți voie să aveți două clase de vizualizare pentru același ViewModel.

Inversarea controlului

Deoarece Xamarin oferă doar pachete C# în jurul API-urilor native, nu oferă nicio formă de mecanism de injectare a dependenței (DI) sau inversare a controlului (IoC).

Fără injectarea de dependențe în timpul de execuție sau injectarea în timp de compilare, nu este ușor să creați componente slab cuplate, reutilizabile, testabile și ușor de întreținut. Ideea de IoC și DI este cunoscută de foarte mult timp; detalii despre IoC pot fi găsite în multe articole online. Puteți afla mai multe despre aceste modele din articolul introductiv al lui Martin Fowler.

IoC a fost disponibil încă din versiunile inițiale ale MvvmCrosses și permite injectarea dependențelor în timpul execuției atunci când pornește aplicația și ori de câte ori sunt necesare.

Pentru a obține componente slab cuplate, nu ar trebui niciodată să avem nevoie de implementări concrete ale claselor. Solicitarea unor implementări concrete limitează capacitatea de a schimba comportamentul implementărilor în timpul rulării (nu o puteți înlocui cu o altă implementare). Testarea acestor componente este dificilă.

Din acest motiv, vom declara o interfață pentru care vom avea o implementare concretă.

 public interface IPasswordGeneratorService { string Generate(int length); }

Și implementare:

 public class PasswordGeneratorService : IPasswordGeneratorService { public string Generate(int length) { var val; var res = new StringBuilder(); var rnd = new Random(); while (0 < length--) { res.Append(valid[rnd.Next(valid.Length)]); } return res.ToString(); } }

ViewModelul nostru poate necesita acum o instanță a interfeței IPasswordGenerationService , pe care suntem responsabili pentru furnizarea.

Pentru ca MvvmCross să injecteze o implementare PasswordGeneratorService în timpul execuției, trebuie să îi spunem MvvmCross ce implementare să folosească. Dacă dorim să folosim o singură implementare pentru ambele platforme, putem înregistra implementările în App.cs , după înregistrarea aplicației:

 public override void Initialize() { RegisterAppStart(new AppStart()); Mvx.LazyConstructAndRegisterSingleton<IPasswordGeneratorService, PasswordGeneratorService>(); }

Apelul de mai sus la metoda statică LazyConstructAndRegisterSingleton<TInterface, TType> înregistrează implementarea care urmează să fie injectată. Această metodă înregistrează implementarea adecvată, dar nu creează un obiect.

Obiectul este creat doar atunci când este necesar și o singură dată, deoarece este înregistrat ca singleton.

Dacă dorim să creăm un obiect singleton imediat, acesta poate fi realizat apelând Mvx.RegisterSingleton<TInterface>() .

Există cazuri în care nu dorim să avem doar singletonuri în aplicația noastră. Obiectul nostru poate să nu fie sigur pentru fire sau poate exista un alt motiv pentru care dorim să avem întotdeauna o instanță nouă. Dacă acesta este cazul, MvvmCross oferă metoda Mvx.RegisterType<TInterface,TType>() , care poate fi folosită pentru a înregistra implementarea într-un mod care instanțiază o nouă instanță ori de câte ori este necesar.

În cazul în care trebuie să furnizați implementări concrete separate pentru fiecare platformă, puteți face oricând acest lucru în proiectele specifice platformei:

 public class DroidPasswodGeneratorService : IPasswordGeneratorService { public string Generate(int length) { return "DroidPasswordGenerator"; } }

Și înregistrarea implementării noastre se face în clasa Setup.cs din proiectul Droid:

 protected override void InitializePlatformServices() { base.InitializePlatformServices(); Mvx.LazyConstructAndRegisterSingleton<IPasswordGeneratorService, DroidPasswodGeneratorService>(); }

După inițializarea codului PCL, MvvmCross va apela InitializePlatformServices și va înregistra implementările serviciilor specifice platformei noastre.

Când înregistrăm mai multe implementări singleton, MvvmCross va folosi doar implementarea care a fost înregistrată ultima. Toate celelalte înregistrări vor fi eliminate.

Creați aplicații pe mai multe platforme cu Xamarin

În acest articol, ați văzut cum Xamarin vă permite să partajați cod pe diferite platforme și să păstrați în continuare senzația nativă și performanța aplicațiilor.

MvvmCross oferă un alt strat de abstractizare, îmbunătățind și mai mult experiența de construire a aplicațiilor multiplatforme cu Xamarin. Modelul MVVM oferă o modalitate de a crea fluxuri de navigare și interacțiune cu utilizatorul care sunt comune pentru toate platformele, limitând cantitatea de cod specific platformei pe care trebuie să o scrieți doar la vizualizări.

Sper că acest articol v-a oferit un motiv pentru a arunca o privire în Xamarin și v-a motivat să vă creați următoarea aplicație multiplatformă cu el.