Část 5 – Praktické strategie sdílení kódu

V této části najdete příklady sdílení kódu pro běžné scénáře aplikací.

Datová vrstva

Datová vrstva se skládá z modulu úložiště a metod pro čtení a zápis informací. Pro zajištění výkonu, flexibility a kompatibility napříč platformami se doporučuje databázový stroj SQLite pro multiplatformní aplikace Xamarinu. Běží na nejrůznějších platformách, včetně Windows, Androidu, iOS a Mac.

SQLite

SQLite je opensourcová implementace databáze. Zdroj a dokumentace najdete v SQLite.org. Podpora SQLite je dostupná na každé mobilní platformě:

  • iOS – integrovaný do operačního systému.
  • Android – integrovaný do operačního systému od Androidu 2.2 (api level 10).
  • Windows – Viz rozšíření SQLite pro Univerzální platforma Windows.

I s databázovým strojem dostupným na všech platformách se nativní metody pro přístup k databázi liší. IOS i Android nabízejí integrovaná rozhraní API pro přístup k rozhraníM SQLite, která se dají použít z Xamarin.iOS nebo Xamarin.Androidu, ale použití nativních metod sady SDK nenabízí možnost sdílet kód (kromě samotných dotazů SQL za předpokladu, že jsou uložené jako řetězce). Podrobnosti o nativních funkcích databáze najdete CoreData ve třídě iOS nebo Androidu SQLiteOpenHelper , protože tyto možnosti nejsou pro různé platformy nad rámec tohoto dokumentu.

ADO.NET

Podpora System.Data Xamarin.iOS i Xamarin.Android a Mono.Data.Sqlite (další informace najdete v dokumentaci k Xamarin.iOS). Použití těchto oborů názvů umožňuje psát ADO.NET kód, který funguje na obou platformách. Upravte odkazy projektu tak, aby zahrnovaly System.Data.dll a Mono.Data.Sqlite.dll přidaly tyto příkazy using do kódu:

using System.Data;
using Mono.Data.Sqlite;

Pak bude fungovat následující ukázkový kód:

string dbPath = Path.Combine (
        Environment.GetFolderPath (Environment.SpecialFolder.Personal),
        "items.db3");
bool exists = File.Exists (dbPath);
if (!exists)
    SqliteConnection.CreateFile (dbPath);
var connection = new SqliteConnection ("Data Source=" + dbPath);
connection.Open ();
if (!exists) {
    // This is the first time the app has run and/or that we need the DB.
    // Copy a "template" DB from your assets, or programmatically create one like this:
    var commands = new[]{
        "CREATE TABLE [Items] (Key ntext, Value ntext);",
        "INSERT INTO [Items] ([Key], [Value]) VALUES ('sample', 'text')"
    };
    foreach (var command in commands) {
        using (var c = connection.CreateCommand ()) {
            c.CommandText = command;
            c.ExecuteNonQuery ();
        }
    }
}
// use `connection`... here, we'll just append the contents to a TextView
using (var contents = connection.CreateCommand ()) {
    contents.CommandText = "SELECT [Key], [Value] from [Items]";
    var r = contents.ExecuteReader ();
    while (r.Read ())
        Console.Write("\n\tKey={0}; Value={1}",
                r ["Key"].ToString (),
                r ["Value"].ToString ());
}
connection.Close ();

Skutečné implementace ADO.NET by se samozřejmě rozdělily mezi různé metody a třídy (tento příklad je pouze pro demonstrační účely).

SQLite-NET – Multiplatformní ORM

ORM (neboli objektově-relační mapovač) se pokouší zjednodušit ukládání dat modelovaných ve třídách. Místo ručního zápisu dotazů SQL, které vytvářejí objekty TABLEs nebo SELECT, vložte a ODSTRAŇte data, která se ručně extrahují z polí a vlastností třídy, přidá ORM vrstvu kódu, která to udělá za vás. Pomocí reflexe pro zkoumání struktury tříd může ORM automaticky vytvářet tabulky a sloupce, které odpovídají třídě, a generovat dotazy pro čtení a zápis dat. To umožňuje kódu aplikace jednoduše odesílat a načítat instance objektů do ORM, což se postará o všechny operace SQL pod kapotou.

SQLite-NET funguje jako jednoduchý ORM, který vám umožní ukládat a načítat třídy v SQLite. Skrývá složitost přístupu SQLite pro různé platformy pomocí kombinace direktiv kompilátoru a dalších triků.

Funkce SQLite-NET:

  • Tabulky jsou definovány přidáním atributů do tříd modelu.
  • Instance databáze je reprezentována podtřídou SQLiteConnection třídy , hlavní třídy v knihovně SQLite-Net.
  • Data lze vložit, dotazovat a odstranit pomocí objektů. Nejsou vyžadovány žádné příkazy SQL (i když v případě potřeby můžete psát příkazy SQL).
  • Základní dotazy Linq lze provádět na kolekcích vrácených rozhraním SQLite-NET.

Zdrojový kód a dokumentace pro SQLite-NET jsou k dispozici na webu SQLite-Net na GitHubu a byly implementovány v obou případových studiích. Níže je uveden jednoduchý příklad kódu SQLite-NET (z případové studie Tasky Pro ).

TodoItem Třída nejprve pomocí atributů definuje pole, které má být primárním klíčem databáze:

public class TodoItem : IBusinessEntity
{
    public TodoItem () {}
    [PrimaryKey, AutoIncrement]
    public int ID { get; set; }
    public string Name { get; set; }
    public string Notes { get; set; }
    public bool Done { get; set; }
}

To umožňuje TodoItem vytvoření tabulky s následujícím řádkem kódu (a bez příkazů SQL) v SQLiteConnection instanci:

CreateTable<TodoItem> ();

Data v tabulce lze také manipulovat s jinými metodami ( SQLiteConnection opět bez vyžadování příkazů SQL):

Insert (TodoItem); // 'task' is an instance with data populated in its properties
Update (TodoItem); // Primary Key field must be populated for Update to work
Table<TodoItem>.ToList(); // returns all rows in a collection

Kompletní příklady najdete ve zdrojovém kódu případové studie.

Přístup k souborům

Přístup k souborům je určitě klíčovou součástí jakékoli aplikace. Mezi běžné příklady souborů, které můžou být součástí aplikace, patří:

  • Soubory databáze SQLite.
  • Uživatelsky generovaná data (text, obrázky, zvuk, video).
  • Stažená data pro ukládání do mezipaměti (obrázky, soubory HTML nebo PDF)

System.IO přímý přístup

Xamarin.iOS i Xamarin.Android umožňují přístup k systému souborů pomocí tříd v System.IO oboru názvů.

Každá platforma má různá omezení přístupu, která je potřeba vzít v úvahu:

  • Aplikace pro iOS běží v sandboxu s velmi omezeným přístupem k systému souborů. Apple dále určuje, jak byste měli systém souborů používat zadáním určitých umístění zálohovaných (a jiných, které nejsou). Další podrobnosti najdete v příručce Práce se systémem souborů v Xamarin.iOS.
  • Android také omezuje přístup k určitým adresářům souvisejícím s aplikací, ale podporuje také externí média (např. Karty SD) a přístup ke sdíleným datům
  • Windows Telefon 8 (Silverlight) nepovolují přímý přístup k souborům – soubory lze manipulovat pouze pomocí IsolatedStorage.
  • Projekty Windows 8.1 WinRT a Windows 10 UPW nabízejí pouze asynchronní operace se soubory prostřednictvím Windows.Storage rozhraní API, která se liší od ostatních platforem.

Příklad pro iOS a Android

Triviální příklad, který zapisuje a čte textový soubor, je znázorněn níže. Použití Environment.GetFolderPath umožňuje, aby stejný kód běžel v iOSu a Androidu, který každý vrací platný adresář na základě jejich konvencí systému souborů.

string filePath = Path.Combine (
        Environment.GetFolderPath (Environment.SpecialFolder.Personal),
        "MyFile.txt");
System.IO.File.WriteAllText (filePath, "Contents of text file");
Console.WriteLine (System.IO.File.ReadAllText (filePath));

Další informace o funkcích systémusouborůch Při psaní přístupového kódu k souborům napříč platformami mějte na paměti, že některé systémy souborů rozlišují malá a velká písmena a mají různé oddělovače adresářů. Při vytváření cest k souborům nebo adresářům je vhodné vždy používat stejná velikost znaků pro názvy souborů a metodu Path.Combine() .

Windows.Storage pro Windows 8 a Windows 10

The Creating Mobile Apps with Xamarin.FormsbookChapter 20. Asynchronní vstupně-výstupní operace a vstupně-výstupní operace souborů zahrnují ukázky pro Windows 8.1 a Windows 10.

DependencyService Pomocí podporovaných rozhraní API je možné číst a souborovat soubory na těchto platformách:

StorageFolder localFolder = ApplicationData.Current.LocalFolder;
IStorageFile storageFile = await localFolder.CreateFileAsync("MyFile.txt",
                                        CreationCollisionOption.ReplaceExisting);
await FileIO.WriteTextAsync(storageFile, "Contents of text file");

Další podrobnosti najdete v kapitole knihy 20 .

Izolované úložiště ve Windows Telefon 7 a 8 (Silverlight)

Izolované úložiště je běžné rozhraní API pro ukládání a načítání souborů na všech platformách iOS, Android a starších platforem Windows Telefon.

Jedná se o výchozí mechanismus pro přístup k souborům ve Windows Telefon (Silverlight), který byl implementován v Xamarin.iOS a Xamarin.Android, aby bylo možné zapisovat běžný kód pro přístup k souborům. Na System.IO.IsolatedStorage třídu lze odkazovat na všechny tři platformy ve sdíleném projektu.

Další informace najdete v přehledu izolovaného úložiště pro Windows Telefon.

Rozhraní API izolovaného úložiště nejsou k dispozici v přenosných knihovnách tříd. Jednou z alternativ pro PCL je NUGet PCLStorage.

Přístup k souborům napříč platformami v souborech PCLS

K dispozici je také nuGet kompatibilní s PCL – PCLStorage – který umožňuje přístup k souborům napříč platformami pro platformy podporované Xamarinem a nejnovější rozhraní API systému Windows.

Síťové operace

Většina mobilních aplikací bude mít například síťovou komponentu:

  • Stahování obrázků, videa a zvuku (např. miniatury, fotky, hudba).
  • Stahování dokumentů (např. HTML, PDF).
  • Nahrávání uživatelských dat (například fotek nebo textu)
  • Přístup k webovým službám nebo rozhraním API třetích stran (včetně SOAP, XML nebo JSON)

Rozhraní .NET Framework poskytuje několik různých tříd pro přístup k síťovým prostředkům: HttpClient, WebClienta HttpWebRequest.

HttpClient

Třída HttpClient v System.Net.Http oboru názvů je k dispozici v Xamarin.iOS, Xamarin.Android a většině platforem Windows. K přenesení tohoto rozhraní API do přenosných knihoven tříd (a technologie Windows Telefon 8 Silverlight) je k dispozici balíček NuGet klientské knihovny Microsoft HTTP.

var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://xamarin.com");
var response = await myClient.SendAsync(request);

Webclient

Třída WebClient poskytuje jednoduché rozhraní API pro načtení vzdálených dat ze vzdálených serverů.

Univerzální platforma Windows operace musí být asynchronní, i když Xamarin.iOS a Xamarin.Android podporují synchronní operace (které je možné provádět na podprocesech na pozadí).

Kód pro jednoduchou asychronous WebClient operaci je:

var webClient = new WebClient ();
webClient.DownloadStringCompleted += (sender, e) =>
{
    var resultString = e.Result;
    // do something with downloaded string, do UI interaction on main thread
};
webClient.Encoding = System.Text.Encoding.UTF8;
webClient.DownloadStringAsync (new Uri ("http://some-server.com/file.xml"));

WebClient také má DownloadFileCompleted a DownloadFileAsync pro načítání binárních dat.

HttpWebRequest

HttpWebRequest nabízí více přizpůsobení než WebClient a v důsledku toho vyžaduje více kódu, který se má použít.

Kód jednoduché synchronní HttpWebRequest operace je:

var request = HttpWebRequest.Create(@"http://some-server.com/file.xml ");
request.ContentType = "text/xml";
request.Method = "GET";
using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
{
    if (response.StatusCode != HttpStatusCode.OK)
        Console.WriteLine("Error fetching data. Server returned status code: {0}", response.StatusCode);
    using (StreamReader reader = new StreamReader(response.GetResponseStream()))
    {
        var content = reader.ReadToEnd();
        // do something with downloaded string, do UI interaction on main thread
    }
}

Příklad najdete v dokumentaci k webovým službám.

Dosažitelnost

Mobilní zařízení fungují v různých síťových podmínkách od rychlých připojení Wi-Fi nebo 4G až po špatné oblasti příjmu a pomalé datové propojení EDGE. Z tohoto důvodu je vhodné před pokusem o připojení ke vzdáleným serverům zjistit, jestli je síť dostupná, a pokud ano, jaký typ sítě je k dispozici.

Mezi akce, které může mobilní aplikace provádět v těchto situacích, patří:

  • Pokud síť není k dispozici, poraďte uživateli. Pokud ho ručně zakázal (např. Režim v letadle nebo vypnutí Wi-Fi) pak můžou tento problém vyřešit.
  • Pokud je připojení 3G, můžou se aplikace chovat jinak (například Apple neumožňuje stažení aplikací větších než 20 Mb přes 3G). Aplikace mohou tyto informace použít k upozornění uživatele na nadměrné doby stahování při načítání velkých souborů.
  • I v případě, že je síť dostupná, je vhodné před zahájením jiných požadavků ověřit připojení k cílovému serveru. Zabráníte tak opakovanému načasování síťových operací aplikace a umožníte uživateli zobrazit informativní chybovou zprávu.

Webservices

Podívejte se na naši dokumentaci k práci s webovými službami, která se zabývá přístupem ke koncovým bodům REST, SOAP a WCF pomocí Xamarin.iOS. Je možné ručně vytvořit žádosti o webové služby a analyzovat odpovědi, ale k dispozici jsou knihovny, které to výrazně zjednodušují, včetně Azure, RestSharp a ServiceStack. V aplikacích Xamarin je možné přistupovat i k základním operacím WCF.

Azure

Microsoft Azure je cloudová platforma, která poskytuje širokou škálu služeb pro mobilní aplikace, včetně úložiště dat a synchronizace a nabízených oznámení.

Navštivte azure.microsoft.com vyzkoušet zdarma.

RestSharp

RestSharp je knihovna .NET, která může být součástí mobilních aplikací, aby poskytovala klienta REST, který zjednodušuje přístup k webovým službám. Pomáhá tím, že poskytuje jednoduché rozhraní API pro vyžádání dat a parsování odpovědi REST. RestSharp může být užitečné

Web RestSharp obsahuje dokumentaci k implementaci klienta REST pomocí RestSharp. RestSharp poskytuje příklady Xamarin.iOS a Xamarin.Android na GitHubu.

V dokumentaci k webovým službám je také fragment kódu Xamarin.iOS.

ServiceStack

Na rozdíl od RestSharp je ServiceStack řešení na straně serveru pro hostování webové služby i klientské knihovny, které je možné implementovat v mobilních aplikacích pro přístup k těmto službám.

Web ServiceStack vysvětluje účel projektu a odkazuje na dokumenty a ukázky kódu. Mezi příklady patří kompletní implementace webové služby na straně serveru a také různé aplikace na straně klienta, které k ní mají přístup.

WCF

Nástroje Xamarin vám můžou pomoct využívat některé služby WCF (Windows Communication Foundation). Xamarin obecně podporuje stejnou podmnožinu WCF na straně klienta, která se dodává s modulem runtime Silverlight. Patří sem nejběžnější implementace kódování a protokolu WCF: textově kódované zprávy SOAP přes přenosový protokol HTTP pomocí BasicHttpBindingprotokolu .

Vzhledem k velikosti a složitosti architektury WCF může existovat aktuální a budoucí implementace služeb, které budou spadat mimo rozsah podporovaný doménou klient-podmnožina Xamarin. Kromě toho podpora WCF vyžaduje použití nástrojů, které jsou k dispozici pouze v prostředí Systému Windows k vygenerování proxy serveru.

Dělení na vlákna

Odezva aplikace je důležitá pro mobilní aplikace – uživatelé očekávají, že se aplikace načítají a provádějí rychle. Zdá se, že se zobrazí zablokovaná obrazovka, která přestane přijímat uživatelský vstup, značí chybové ukončení aplikace, takže je důležité nevázat vlákno uživatelského rozhraní s dlouhotrvajícími blokujícími voláními, jako jsou síťové požadavky nebo pomalé místní operace (například rozbalení souboru). Zejména spouštěcí proces by neměl obsahovat dlouhotrvající úlohy – všechny mobilní platformy zabíjejí aplikaci, která trvá příliš dlouho.

To znamená, že vaše uživatelské rozhraní by mělo implementovat indikátor průběhu nebo jinak použitelné uživatelské rozhraní, které je rychlé k zobrazení, a asynchronní úlohy pro provádění operací na pozadí. Provádění úloh na pozadí vyžaduje použití vláken, což znamená, že úlohy na pozadí potřebují způsob, jak komunikovat zpět s hlavním vláknem, aby bylo možné označit průběh nebo po dokončení.

Knihovna paralelních úloh

Úlohy vytvořené pomocí knihovny paralelních úloh můžou běžet asynchronně a vracet se na volající vlákno, což je velmi užitečné pro aktivaci dlouhotrvajících operací bez blokování uživatelského rozhraní.

Jednoduchá paralelní operace úlohy může vypadat takto:

using System.Threading.Tasks;
void MainThreadMethod ()
{
    Task.Factory.StartNew (() => wc.DownloadString ("http://...")).ContinueWith (
        t => label.Text = t.Result, TaskScheduler.FromCurrentSynchronizationContext()
    );
}

Klíč je TaskScheduler.FromCurrentSynchronizationContext() , který znovu použije SynchronizationContext.Current vlákna volání metody (zde hlavní vlákno, které běží MainThreadMethod) jako způsob zařazování volání zpět do tohoto vlákna. To znamená, že pokud je metoda volána ve vlákně uživatelského rozhraní, spustí ContinueWith operaci zpět ve vlákně uživatelského rozhraní.

Pokud kód spouští úlohy z jiných vláken, pomocí následujícího vzoru vytvořte odkaz na vlákno uživatelského rozhraní a úloha se k němu může stále volat:

static Context uiContext = TaskScheduler.FromCurrentSynchronizationContext();

Vyvolání ve vlákně uživatelského rozhraní

Pro kód, který nevyužívá knihovnu paralelních úloh, má každá platforma vlastní syntaxi pro zařazování operací zpět do vlákna uživatelského rozhraní:

  • iOSowner.BeginInvokeOnMainThread(new NSAction(action))
  • Androidowner.RunOnUiThread(action)
  • Xamarin.FormsDevice.BeginInvokeOnMainThread(action)
  • WindowsDeployment.Current.Dispatcher.BeginInvoke(action)

Syntaxe iOSu i Androidu vyžaduje, aby byla k dispozici třída context, což znamená, že kód musí předat tento objekt do všech metod, které vyžadují zpětné volání ve vlákně uživatelského rozhraní.

Pokud chcete volat vlákno uživatelského rozhraní ve sdíleném kódu, postupujte podle příkladu IDispatchOnUIThread (se svolením @follesoe). Deklarujte a programujte do rozhraní ve sdíleném IDispatchOnUIThread kódu a pak implementujte třídy specifické pro platformu, jak je znázorněno zde:

// program to the interface in shared code
public interface IDispatchOnUIThread {
    void Invoke (Action action);
}
// iOS
public class DispatchAdapter : IDispatchOnUIThread {
    public readonly NSObject owner;
    public DispatchAdapter (NSObject owner) {
        this.owner = owner;
    }
    public void Invoke (Action action) {
        owner.BeginInvokeOnMainThread(new NSAction(action));
    }
}
// Android
public class DispatchAdapter : IDispatchOnUIThread {
    public readonly Activity owner;
    public DispatchAdapter (Activity owner) {
        this.owner = owner;
    }
    public void Invoke (Action action) {
        owner.RunOnUiThread(action);
    }
}
// WP7
public class DispatchAdapter : IDispatchOnUIThread {
    public void Invoke (Action action) {
        Deployment.Current.Dispatcher.BeginInvoke(action);
    }
}

Vývojáři Xamarin.Forms by měli používat Device.BeginInvokeOnMainThread společný kód (sdílené projekty nebo PCL).

Možnosti a snížení výkonu platformy a zařízení

Další konkrétní příklady práce s různými možnostmi jsou uvedeny v dokumentaci k funkcím platformy. Zabývá se zjišťováním různých funkcí a s tím, jak elegantně snížit výkon aplikace tak, aby poskytovala dobré uživatelské prostředí, i když aplikace nemůže fungovat s úplným potenciálem.