Případová studie multiplatformních aplikací: Tasky
Tasky Portable je jednoduchá aplikace seznamu úkolů. Tento dokument popisuje, jak byla navržena a sestavena, a to podle pokynů v dokumentu o vytváření multiplatformních aplikací . Diskuze se zabývá následujícími oblastmi:
Proces návrhu
Před zahájením psaní kódu je vhodné vytvořit roadmapu pro to, co chcete dosáhnout. To platí zejména pro vývoj napříč platformami, kde vytváříte funkce, které budou vystaveny několika způsoby. Počínaje jasnou představu o tom, co vytváříte, šetří čas a úsilí později ve vývojovém cyklu.
Požadavky
Prvním krokem při návrhu aplikace je identifikace požadovaných funkcí. Můžou to být cíle vysoké úrovně nebo podrobné případy použití. Úloha má jednoduché funkční požadavky:
- Zobrazení seznamu úkolů
- Přidání, úprava a odstranění úkolů
- Nastavení stavu úkolu na hotovo
Měli byste zvážit použití funkcí specifických pro konkrétní platformu. Může úloha využívat geofencing iOS nebo živé dlaždice Windows Phone Live? I když v první verzi nepoužíváte funkce specifické pro danou platformu, měli byste předem naplánovat, abyste měli jistotu, že je vaše obchodní vrstvy a vrstvy dat můžou pojmout.
Návrh uživatelského rozhraní
Začněte s návrhem vysoké úrovně, který lze implementovat napříč cílovými platformami. Poznamenejte si omezení uživatelského rozhraní specifikované platformou. Například v iOSu TabBarController
může zobrazit více než pět tlačítek, zatímco ekvivalent Windows Phone může zobrazit až čtyři.
Kreslete tok obrazovky pomocí nástroje podle vašeho výběru (papírová práce).
Datový model
Znalost toho, jaká data je potřeba uložit, vám pomůže určit, jaký mechanismus trvalosti se má použít. Informace o dostupných mechanismech úložiště a rozhodování mezi nimi najdete v tématu Přístup k datům napříč platformami. Pro tento projekt budeme používat SQLite.NET.
Úloha musí uložit tři vlastnosti pro každou vlastnost TaskItem:
- Name – řetězec
- Poznámky – řetězec
- Hotovo – logická hodnota
Základní funkce
Vezměte v úvahu rozhraní API, které bude uživatelské rozhraní muset využívat, aby splňovalo požadavky. Seznam úkolů vyžaduje následující funkce:
- Zobrazení seznamu všech úkolů – zobrazení hlavního seznamu dostupných úkolů
- Získání jednoho úkolu – při dotyku řádku úkolu
- Uložení jednoho úkolu – při úpravě úkolu
- Odstranění jednoho úkolu – při odstranění úkolu
- Vytvoření prázdného úkolu – při vytvoření nového úkolu
Pokud chcete dosáhnout opětovného použití kódu, toto rozhraní API by mělo být implementováno jednou v knihovně přenosných tříd.
Implementace
Po odsouhlasení návrhu aplikace zvažte, jak se dá implementovat jako multiplatformní aplikace. Stane se to architekturou aplikace. Podle pokynů v dokumentu Vytváření multiplatformních aplikací by měl být kód aplikace rozdělen do následujících částí:
- Společný kód – běžný projekt, který obsahuje opakovaně použitelný kód pro ukládání dat úkolů, zveřejnění třídy modelu a rozhraní API pro správu ukládání a načítání dat.
- Kód specifický pro platformu – projekty specifické pro platformu, které implementují nativní uživatelské rozhraní pro každý operační systém a využívají společný kód jako back-end.
Tyto dvě části jsou popsány v následujících částech.
Běžný kód (PCL)
Tasky Portable používá strategii knihovny přenosných tříd ke sdílení společného kódu. Popis možností sdílení kódu najdete v dokumentu Možnosti sdílení kódu.
Veškerý společný kód, včetně vrstvy přístupu k datům, databázového kódu a kontraktů, se umístí do projektu knihovny.
Kompletní projekt PCL je znázorněn níže. Veškerý kód v přenosné knihovně je kompatibilní s každou cílovou platformou. Po nasazení bude každá nativní aplikace odkazovat na danou knihovnu.
Následující diagram tříd znázorňuje třídy seskupené podle vrstev. Třída SQLiteConnection
je často používaný kód z balíčku Sqlite-NET. Zbývající třídy jsou vlastní kód pro Tasky. TaskItem
Třídy TaskItemManager
představují rozhraní API, které je vystaveno aplikacím pro konkrétní platformu.
Použití oborů názvů k oddělení vrstev pomáhá spravovat odkazy mezi jednotlivými vrstvami. Projekty specifické pro platformu by měly obsahovat using
pouze prohlášení pro obchodní vrstvu. Vrstva přístupu k datům a datová vrstva by měla být zapouzdřena rozhraním API, které je vystaveno TaskItemManager
v obchodní vrstvě.
Reference
Přenosné knihovny tříd musí být použitelné na různých platformách, z nichž každá má různou úroveň podpory pro funkce platformy a architektury. Z tohoto důvodu existují omezení, která balíčky a knihovny architektury lze použít. Například Xamarin.iOS nepodporuje klíčové slovo c# dynamic
, takže přenosná knihovna tříd nemůže použít žádný balíček, který závisí na dynamickém kódu, i když by takový kód fungoval na Androidu. Visual Studio pro Mac vám zabrání v přidávání nekompatibilních balíčků a odkazů, ale budete chtít mít na paměti omezení, abyste se vyhnuli překvapením později.
Poznámka: Uvidíte, že vaše projekty odkazují na knihovny architektury, které jste nepoužili. Tyto odkazy jsou součástí šablon projektů Xamarin. Když jsou aplikace zkompilovány, proces propojování odebere neodkazovaný kód, takže i když System.Xml
byl odkazován, nebude zahrnut do konečné aplikace, protože nepoužíváme žádné funkce XML.
Datová vrstva (DL)
Datová vrstva obsahuje kód, který slouží k fyzickému ukládání dat – ať už do databáze, plochých souborů nebo jiného mechanismu. Datová vrstva Tasky se skládá ze dvou částí: knihovny SQLite-NET a vlastního kódu přidaného k jeho připojení.
Úloha spoléhá na balíček NuGet Sqlite-net (publikovaný Frankem Kruegerem) k vložení kódu SQLite-NET, který poskytuje databázové rozhraní ORM (Object-Relational Mapping). Třída TaskItemDatabase
dědí z SQLiteConnection
a přidá požadované metody Create, Read, Update, Delete (CRUD) pro čtení a zápis dat do SQLite. Jedná se o jednoduchou častou implementaci obecných metod CRUD, které je možné znovu použít v jiných projektech.
Jedná se TaskItemDatabase
o jediný typ, který zajišťuje, že k veškerému přístupu dojde ve stejné instanci. Zámek slouží k zabránění souběžnému přístupu z více vláken.
SQLite ve Windows Phone
IOS i Android jsou součástí operačního systému iOS i Android, ale windows Phone neobsahuje kompatibilní databázový stroj. Ke sdílení kódu na všech třech platformách se vyžaduje nativní verze SQLite pro Windows. Další informace o nastavení projektu Windows Phone pro Sqlite najdete v tématu Práce s místní databází .
Použití rozhraní ke generalizaci přístupu k datům
Datová vrstva přebírá závislost, BL.Contracts.IBusinessIdentity
aby mohl implementovat abstraktní metody přístupu k datům, které vyžadují primární klíč. Každá třída obchodní vrstvy, která implementuje rozhraní, je pak možné zachovat v datové vrstvě.
Rozhraní pouze určuje celočíselnou vlastnost, která se má chovat jako primární klíč:
public interface IBusinessEntity {
int ID { get; set; }
}
Základní třída implementuje rozhraní a přidá atributy SQLite-NET, které je označí jako automaticky inkrementující primární klíč. Všechny třídy v obchodní vrstvě, která implementuje tuto základní třídu, je pak možné zachovat v datové vrstvě:
public abstract class BusinessEntityBase : IBusinessEntity {
public BusinessEntityBase () {}
[PrimaryKey, AutoIncrement]
public int ID { get; set; }
}
Příkladem obecných metod ve vrstvě dat, která používají rozhraní, je tato GetItem<T>
metoda:
public T GetItem<T> (int id) where T : BL.Contracts.IBusinessEntity, new ()
{
lock (locker) {
return Table<T>().FirstOrDefault(x => x.ID == id);
}
}
Zamykání, aby se zabránilo souběžnému přístupu
Zámek je implementován v rámci TaskItemDatabase
třídy, aby se zabránilo souběžnému přístupu k databázi. Tím zajistíte, aby byl serializován souběžný přístup z různých vláken (jinak se komponenta uživatelského rozhraní může pokusit o čtení databáze ve stejnou dobu, kdy se vlákno na pozadí aktualizuje). Tady je příklad implementace zámku:
static object locker = new object ();
public IEnumerable<T> GetItems<T> () where T : BL.Contracts.IBusinessEntity, new ()
{
lock (locker) {
return (from i in Table<T> () select i).ToList ();
}
}
public T GetItem<T> (int id) where T : BL.Contracts.IBusinessEntity, new ()
{
lock (locker) {
return Table<T>().FirstOrDefault(x => x.ID == id);
}
}
Většinu kódu datové vrstvy je možné znovu použít v jiných projektech. Jediný kód specifický pro aplikaci ve vrstvě je CreateTable<TaskItem>
volání v konstruktoru TaskItemDatabase
.
Vrstva přístupu k datům (DAL)
Třída TaskItemRepository
zapouzdřuje mechanismus úložiště dat pomocí rozhraní API silného typu, které umožňuje TaskItem
vytvářet, odstraňovat, načítat a aktualizovat objekty.
Použití podmíněné kompilace
Třída používá podmíněnou kompilaci k nastavení umístění souboru - to je příklad implementace platform divergence. Vlastnost, která vrací cestu zkompiluje do jiného kódu na každé platformě. Tady jsou uvedené direktivy kompilátoru specifické pro kód a platformu:
public static string DatabaseFilePath {
get {
var sqliteFilename = "TaskDB.db3";
#if SILVERLIGHT
// Windows Phone expects a local path, not absolute
var path = sqliteFilename;
#else
#if __ANDROID__
// Just use whatever directory SpecialFolder.Personal returns
string libraryPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal); ;
#else
// 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"); // Library folder
#endif
var path = Path.Combine (libraryPath, sqliteFilename);
#endif
return path;
}
}
V závislosti na platformě bude výstup "<app path>/Library/TaskDB.db3" pro iOS, "<app path>/Documents/TaskDB.db3" pro Android nebo jen "TaskDB.db3" pro Windows Phone.
Obchodní vrstva (BL)
Obchodní vrstva implementuje třídy modelu a façade pro jejich správu.
V Tasky model je TaskItem
třída a TaskItemManager
implementuje model Façade, který poskytuje rozhraní API pro správu TaskItems
.
Fasáda
TaskItemManager
zabalí metodu DAL.TaskItemRepository
Get, Save a Delete, na kterou budou odkazovat vrstvy aplikace a uživatelského rozhraní.
Obchodní pravidla a logika by se sem umístily v případě potřeby – například všechna ověřovací pravidla, která musí být splněna před uložením objektu.
Rozhraní API pro kód specifický pro platformu
Po napsání společného kódu musí být uživatelské rozhraní sestaveno, aby bylo možné shromažďovat a zobrazovat data, která jsou vystavena. Třída TaskItemManager
implementuje model Façade, který poskytuje jednoduché rozhraní API pro přístup kódu aplikace.
Kód napsaný v každém projektu specifickém pro platformu bude obecně úzce svázán s nativní sadou SDK daného zařízení a přistupovat pouze ke společnému kódu pomocí rozhraní API definovaného rozhraním TaskItemManager
. To zahrnuje metody a obchodní třídy, které zveřejňuje, například TaskItem
.
Image se nesdílejí napříč platformami, ale do každého projektu se přidávají nezávisle. To je důležité, protože každá platforma zpracovává obrázky odlišně, a to pomocí různých názvů souborů, adresářů a rozlišení.
Zbývající části se týkají podrobností implementace specifické pro danou platformu uživatelského rozhraní Tasky.
Aplikace pro iOS
K implementaci aplikace Tasky pro iOS pomocí společného projektu PCL pro ukládání a načítání dat se vyžaduje pouze několik tříd. Kompletní projekt Xamarin.iOS pro iOS je uvedený níže:
Třídy se zobrazují v tomto diagramu seskupené do vrstev.
Reference
Aplikace pro iOS odkazuje na knihovny SDK specifické pro platformu – např. Xamarin.iOS a MonoTouch.Dialog-1.
Musí také odkazovat na TaskyPortableLibrary
projekt PCL.
Seznam odkazů je zde uvedený:
Aplikační vrstva a vrstva uživatelského rozhraní jsou v tomto projektu implementovány pomocí těchto odkazů.
Aplikační vrstva (AL)
Aplikační vrstva obsahuje třídy specifické pro platformu potřebné k "vytvoření vazby" objektů vystavených pcL do uživatelského rozhraní. Aplikace specifická pro iOS má dvě třídy, které pomáhají zobrazit úlohy:
- EditingSource – Tato třída slouží k vytvoření vazby seznamů úkolů s uživatelským rozhraním. Vzhledem k tomu
MonoTouch.Dialog
, že se používala pro seznam úkolů, musíme implementovat tohoto pomocníka, abychom povolili funkci potáhnutí prstem a odstranění v nástrojiUITableView
. Potažení prstem na odstranění je v iOSu běžné, ale ne Android nebo Windows Phone, takže konkrétní projekt pro iOS je jediný projekt, který ho implementuje. - TaskDialog – Tato třída slouží k vytvoření vazby jednoho úkolu k uživatelskému rozhraní. Používá
MonoTouch.Dialog
rozhraní Reflection API k zabalení objektuTaskItem
třídou, která obsahuje správné atributy, aby vstupní obrazovka byla správně naformátovaná.
Třída TaskDialog
používá MonoTouch.Dialog
atributy k vytvoření obrazovky na základě vlastností třídy. Třída vypadá takto:
public class TaskDialog {
public TaskDialog (TaskItem task)
{
Name = task.Name;
Notes = task.Notes;
Done = task.Done;
}
[Entry("task name")]
public string Name { get; set; }
[Entry("other task info")]
public string Notes { get; set; }
[Entry("Done")]
public bool Done { get; set; }
[Section ("")]
[OnTap ("SaveTask")] // method in HomeScreen
[Alignment (UITextAlignment.Center)]
public string Save;
[Section ("")]
[OnTap ("DeleteTask")] // method in HomeScreen
[Alignment (UITextAlignment.Center)]
public string Delete;
}
Všimněte si, že OnTap
atributy vyžadují název metody – tyto metody musí existovat ve třídě, HomeScreen
ve MonoTouch.Dialog.BindingContext
které je vytvořena (v tomto případě třída popisovaná v další části).
Vrstva uživatelského rozhraní (UI)
Vrstva uživatelského rozhraní se skládá z následujících tříd:
- AppDelegate – Obsahuje volání rozhraní API pro vzhled pro styl písma a barvy použité v aplikaci. Tasky je jednoduchá aplikace, takže neexistují žádné další inicializační úlohy spuštěné v
FinishedLaunching
. - Obrazovky – podtřídy
UIViewController
, které definují každou obrazovku a její chování. Obrazovky spojují uživatelské rozhraní s třídami aplikační vrstvy a běžným rozhraním API (TaskItemManager
). V tomto příkladu se obrazovky vytvářejí v kódu, ale mohly být navrženy pomocí Tvůrce rozhraní Xcode nebo návrháře scénáře. - Obrázky – vizuální prvky jsou důležitou součástí každé aplikace. Tasky má úvodní obrazovku a obrázky ikon, které musí být pro iOS dodávány v běžném rozlišení a sítnici.
Domovská obrazovka
Domovská MonoTouch.Dialog
obrazovka je obrazovka, která zobrazuje seznam úkolů z databáze SQLite. Dědí z DialogViewController
kódu a implementuje kód, který nastaví Root
, aby obsahoval kolekci TaskItem
objektů pro zobrazení.
Mezi dvě hlavní metody související se zobrazením a interakcí se seznamem úkolů patří:
- NaplnitTable – používá metodu obchodní vrstvy
TaskManager.GetTasks
k načtení kolekceTaskItem
objektů k zobrazení. - Vybráno – Když se dotknete řádku, zobrazí úkol na nové obrazovce.
Obrazovka podrobností úkolu
Podrobnosti úkolu jsou vstupní obrazovka, která umožňuje upravovat nebo odstraňovat úkoly.
Tasky používá MonoTouch.Dialog
k zobrazení obrazovky rozhraní API reflexe, takže neexistuje žádná UIViewController
implementace. Místo toho třída HomeScreen
vytvoří instanci a zobrazí DialogViewController
použití TaskDialog
třídy z aplikační vrstvy.
Tento snímek obrazovky ukazuje prázdnou obrazovku Entry
, která ukazuje nastavení textu vodoznaku v polích Název a Poznámky :
Funkce obrazovky Podrobnosti úkolu (například uložení nebo odstranění úkolu) musí být implementována ve HomeScreen
třídě, protože se jedná o místo, MonoTouch.Dialog.BindingContext
kde je vytvořen. Následující HomeScreen
metody podporují obrazovku Podrobnosti úkolu:
- ShowTaskDetails – Vytvoří
MonoTouch.Dialog.BindingContext
obrazovku pro vykreslení. Vytvoří vstupní obrazovku pomocí odrazu k načtení názvů a typů vlastností zeTaskDialog
třídy. Další informace, například text vodoznaku pro vstupní pole, se implementují s atributy vlastností. - SaveTask – Tato metoda je odkazována ve
TaskDialog
třídě prostřednictvím atributuOnTap
. Volá se při stisknutí tlačítka Uložit a používáMonoTouch.Dialog.BindingContext
k načtení uživatelem zadaných dat před uložením změn pomocíTaskItemManager
. - DeleteTask – Tato metoda je odkazována ve
TaskDialog
třídě prostřednictvím atributuOnTap
. PoužíváTaskItemManager
k odstranění dat pomocí primárního klíče (vlastnosti ID).
Aplikace pro Android
Kompletní projekt Xamarin.Android je znázorněný níže:
Diagram tříd se skupinami podle vrstev:
Reference
Projekt aplikace pro Android musí odkazovat na sestavení Xamarin.Android specifické pro platformu pro přístup ke třídám ze sady Android SDK.
Musí také odkazovat na projekt PCL (např. TaskyPortableLibrary) pro přístup k běžným datům a kódu obchodní vrstvy.
Aplikační vrstva (AL)
Podobně jako verze iOS, kterou jsme si prohlédli dříve, vrstva aplikace ve verzi Androidu obsahuje třídy specifické pro platformu potřebné k "vytvoření vazby" objektů vystavených jádrem v uživatelském rozhraní.
TaskListAdapter – k zobrazení seznamu<T> objektů potřebujeme implementovat adaptér pro zobrazení vlastních objektů v objektu ListView
. Adaptér určuje, které rozložení se používá pro každou položku v seznamu – v tomto případě kód používá integrované rozložení SimpleListItemChecked
Androidu .
Uživatelské rozhraní
Vrstva uživatelského rozhraní aplikace pro Android je kombinací kódu a kódu XML.
- Prostředky/rozložení – rozložení obrazovky a návrh buňky řádku implementované jako soubory AXML AXML lze psát ručně nebo vizuálně rozložen pomocí návrháře uživatelského rozhraní Xamarin pro Android.
- Zdroje / Kreslení – obrázky (ikony) a vlastní tlačítko.
- Obrazovky – podtřídy aktivity, které definují každou obrazovku a její chování. Spojí uživatelské rozhraní s třídami aplikační vrstvy a společným rozhraním API (
TaskItemManager
).
Domovská obrazovka
Domovská obrazovka se skládá z podtřídy HomeScreen
aktivity a HomeScreen.axml
souboru, který definuje rozložení (umístění tlačítka a seznamu úkolů). Obrazovka vypadá takto:
Kód domovské obrazovky definuje obslužné rutiny pro kliknutí na tlačítko a kliknutí na položky v seznamu a také naplnění seznamu v OnResume
metodě (aby odrážel změny provedené na obrazovce podrobností úkolu). Data se načítají pomocí obchodní vrstvy TaskItemManager
a TaskListAdapter
z aplikační vrstvy.
Obrazovka podrobností úkolu
Obrazovka podrobností úkolu se také skládá z Activity
podtřídy a souboru rozložení AXML. Rozložení určuje umístění vstupních ovládacích prvků a třída jazyka C# definuje chování pro načtení a uložení TaskItem
objektů.
Všechny odkazy na knihovnu TaskItemManager
PCL jsou prostřednictvím třídy.
Aplikace pro Windows Phone
Kompletní projekt Windows Phone:
Následující diagram znázorňuje třídy seskupené do vrstev:
Reference
Projekt specifický pro platformu musí odkazovat na požadované knihovny specifické pro platformu (například Microsoft.Phone
a System.Windows
) k vytvoření platné aplikace pro Windows Phone.
Musí také odkazovat na projekt PCL (např. TaskyPortableLibrary
) pro využití TaskItem
třídy a databáze.
Aplikační vrstva (AL)
Stejně jako u verzí pro iOS a Android se aplikační vrstva skládá z vizuálů, které pomáhají svázat data s uživatelským rozhraním.
Modely Zobrazení
Modely ViewModels zabalují data z pcL ( TaskItemManager
) a představují je způsobem, který může využívat datová vazba Silverlight/XAML. Toto je příklad chování specifické pro platformu (jak je popsáno v dokumentu o multiplatformních aplikacích).
Uživatelské rozhraní
XAML má jedinečnou funkci vazby dat, která se dá deklarovat v kódu a snížit množství kódu potřebného k zobrazení objektů:
- Stránky – soubory XAML a jejich kódbehind definují uživatelské rozhraní a odkazují na ViewModels a projekt PCL k zobrazení a shromažďování dat.
- Obrázky – úvodní obrazovka, obrázky pozadí a ikon jsou klíčovou součástí uživatelského rozhraní.
MainPage
Třída MainPage používá TaskListViewModel
k zobrazení dat pomocí funkcí datové vazby XAML. Stránka DataContext
je nastavená na model zobrazení, který se vyplní asynchronně. Syntaxe {Binding}
v XAML určuje způsob zobrazení dat.
TaskDetailsPage
Každý úkol je zobrazen vazbou TaskViewModel
na XAML definovaný v TaskDetailsPage.xaml. Data úkolu se načtou TaskItemManager
přes obchodní vrstvu.
Výsledky
Výsledné aplikace vypadají na každé platformě takto:
iOS
Aplikace používá standardní návrh uživatelského rozhraní pro iOS, například tlačítko přidat umístěné na navigačním panelu a pomocí integrované ikony plus (+). Používá také výchozí UINavigationController
chování tlačítka Zpět a podporuje v tabulce potažení prstem a odstranění.
Android
Aplikace pro Android používá integrované ovládací prvky, včetně integrovaného rozložení pro řádky, které vyžadují zobrazení "zaškrtnutí". Kromě tlačítka Zpět na obrazovce se podporuje chování hardwaru nebo systému zpět.
Windows Phone
Aplikace Pro Windows Phone používá standardní rozložení a místo navigačního panelu v horní části obrazovky naplňuje panel aplikací.
Shrnutí
Tento dokument poskytuje podrobné vysvětlení toho, jak byly principy návrhu vrstvené aplikace použity na jednoduchou aplikaci, která usnadňuje opětovné použití kódu na třech mobilních platformách: iOS, Android a Windows Phone.
Popsal proces, který se používá k návrhu aplikačních vrstev a probral, jaké funkce a kód byly implementovány v každé vrstvě.
Kód je možné stáhnout z GitHubu.