Použití knihoven C/C++ s Xamarinem
Přehled
Xamarin umožňuje vývojářům vytvářet mobilní aplikace nativní pro různé platformy pomocí sady Visual Studio. Obecně platí, že vazby jazyka C# slouží k zveřejnění existujících komponent platformy vývojářům. Existují ale chvíle, kdy aplikace Xamarin musí pracovat s existujícími základy kódu. Někdy týmy jednoduše nemají čas, rozpočet nebo prostředky pro přenos velkého dobře testovaného a vysoce optimalizovaného základu kódu do jazyka C#.
Visual C++ pro vývoj multiplatformních mobilních řešení umožňuje vytváření kódu C/C++ a C# jako součásti stejného řešení a nabízí mnoho výhod, včetně sjednoceného prostředí ladění. Microsoft tímto způsobem použil C/C++ a Xamarin k doručování aplikací, jako je Hyperlapse Mobile a Pix Kamera.
V některých případech však existuje touha (nebo požadavek) zachovat stávající nástroje a procesy C/C++ a udržovat kód knihovny oddělený od aplikace, považovat knihovnu za to, jako by byla podobná komponentě třetí strany. V těchto situacích výzva nejen vystavuje relevantní členy jazyka C#, ale správu knihovny jako závislosti. A samozřejmě, automatizace co nejvíce tohoto procesu.
Tento příspěvek popisuje obecný přístup pro tento scénář a provede jednoduchým příkladem.
Pozadí
Jazyk C/C++ se považuje za multiplatformní jazyk, ale je potřeba věnovat velkou pozornost tomu, aby zdrojový kód byl skutečně multiplatformní, a to pomocí pouze jazyka C/C++ podporovaného všemi cílovými kompilátory a obsahujícími málo nebo bez podmíněného zahrnutého kódu nebo kódu specifického pro kompilátor.
Nakonec se musí kód zkompilovat a úspěšně spustit na všech cílových platformách, a proto se tím zredukuje commonality napříč cílovými platformami (a kompilátory). K problémům může docházet i v menších rozdílech mezi kompilátory, takže důkladné testování (nejlépe automatizované) na každé cílové platformě je stále důležitější.
Přístup vysoké úrovně
Následující obrázek představuje čtyřfázový přístup, který se používá k transformaci zdrojového kódu C/C++ do multiplatformní knihovny Xamarin, která se sdílí prostřednictvím NuGetu a pak se využívá v aplikaci Xamarin.Forms.
4 fáze jsou:
- Kompilace zdrojového kódu C/C++ do nativních knihoven specifických pro platformu.
- Zabalení nativních knihoven pomocí řešení sady Visual Studio
- Balení a nabízení balíčku NuGet pro obálku .NET
- Využívání balíčku NuGet z aplikace Xamarin
Fáze 1: Kompilace zdrojového kódu C/C++ do nativních knihoven specifických pro platformu
Cílem této fáze je vytvořit nativní knihovny, které lze volat obálkou jazyka C#. To může nebo nemusí být relevantní v závislosti na vaší situaci. Mnoho nástrojů a procesů, které lze v tomto obvyklém scénáři přinést, jsou nad rámec tohoto článku. Klíčové aspekty jsou udržování základu kódu C/C++ v synchronizaci s jakýmkoli nativním zabaleným kódem, dostatečným testováním jednotek a automatizací sestavení.
Knihovny v návodu byly vytvořeny pomocí editoru Visual Studio Code s doprovodným skriptem prostředí. Rozšířenou verzi tohoto návodu najdete v úložišti Mobile CAT na GitHubu, které tuto část ukázky podrobněji popisuje. Nativní knihovny jsou v tomto případě považovány za závislost třetí strany, ale tato fáze je znázorněna pro kontext.
Pro zjednodušení cílí návod pouze na podmnožinu architektur. Pro iOS používá nástroj lipo k vytvoření jediného tukového binárního souboru z jednotlivých binárních souborů specifických pro architekturu. Android bude používat dynamické binární soubory s příponou .so a iOS použije statický binární soubor tuku s příponou .a.
Fáze 2: Zabalení nativních knihoven pomocí řešení sady Visual Studio
Další fází je zabalení nativních knihoven, aby se snadno používaly z .NET. To se provádí pomocí řešení sady Visual Studio se čtyřmi projekty. Sdílený projekt obsahuje společný kód. Projekty, které cílí na každý z Xamarin.Android, Xamarin.iOS a .NET Standard, umožňují odkazovat na knihovnu nezávislou na platformě.
Obálka používá "návnadu a přepínač trik". To není jediný způsob, ale usnadňuje odkazování na knihovnu a vyhne se nutnosti explicitně spravovat implementace specifické pro platformu v rámci samotné aplikace využívající. Trik v podstatě zajišťuje, aby cíle (.NET Standard, Android, iOS) sdílely stejný obor názvů, název sestavení a strukturu tříd. Vzhledem k tomu, že NuGet bude vždy upřednostňovat knihovnu specifickou pro platformu, verze .NET Standard se nikdy nepoužívá za běhu.
Většina práce v tomto kroku se zaměří na volání metod nativní knihovny a správu odkazů na podkladové objekty pomocí volání metod nativní knihovny. Cílem je zpřístupnit uživatelům funkce knihovny a zároveň se zkompstrahotovat veškerou složitost. Vývojáři Xamarin.Forms nemusí mít znalosti o vnitřních fungováních nespravované knihovny. Mělo by se zdát, že používají jakoukoli jinou spravovanou knihovnu jazyka C#.
Výstupem této fáze je nakonec sada knihoven .NET, jedna na cíl spolu s dokumentem nuspec, který obsahuje informace potřebné k sestavení balíčku v dalším kroku.
Fáze 3: Balení a nabízení balíčku NuGet pro obálku .NET
Třetí fáze vytváří balíček NuGet pomocí artefaktů sestavení z předchozího kroku. Výsledkem tohoto kroku je balíček NuGet, který lze využívat z aplikace Xamarin. Návod používá místní adresář, který slouží jako informační kanál NuGet. V produkčním prostředí by tento krok měl publikovat balíček do veřejného nebo privátního informačního kanálu NuGet a měl by být plně automatizovaný.
Fáze 4: Využívání balíčku NuGet z aplikace Xamarin.Forms
Posledním krokem je odkazování na balíček NuGet z aplikace Xamarin.Forms a jeho použití. To vyžaduje konfiguraci informačního kanálu NuGet v sadě Visual Studio tak, aby používal informační kanál definovaný v předchozím kroku.
Po nakonfigurování informačního kanálu se na balíček musí odkazovat z každého projektu v aplikaci Xamarin.Forms pro různé platformy. Trik bait-and-switch poskytuje identická rozhraní, takže funkce nativní knihovny lze volat pomocí kódu definovaného v jednom umístění.
Úložiště zdrojového kódu obsahuje seznam dalších věcí, které obsahují články o nastavení privátního informačního kanálu NuGet v Azure DevOps a o tom, jak balíček odeslat do tohoto informačního kanálu. I když vyžadujete trochu víc času nastavení než místní adresář, tento typ informačního kanálu je lepší v týmovém vývojovém prostředí.
Průchozí cesta
Uvedené kroky jsou specifické pro Visual Studio pro Mac, ale struktura funguje i v sadě Visual Studio 2017.
Požadavky
Aby mohli postupovat podle tohoto návodu, vývojář bude potřebovat:
Poznámka:
K nasazení aplikací do i Telefon se vyžaduje aktivní vývojářský účet Apple.
Vytvoření nativních knihoven (fáze 1)
Funkce nativní knihovny vychází z příkladu návodu: Vytvoření a použití statické knihovny (C++).
Tento názorný postup přeskočí první fázi a sestaví nativní knihovny, protože knihovna je v tomto scénáři poskytována jako závislost třetí strany. Předkompilované nativní knihovny jsou součástí ukázkového kódu nebo je můžete stáhnout přímo.
Práce s nativní knihovnou
Původní příklad MathFuncsLib obsahuje jednu třídu volanou MyMathFuncs
s následující definicí:
namespace MathFuncs
{
class MyMathFuncs
{
public:
double Add(double a, double b);
double Subtract(double a, double b);
double Multiply(double a, double b);
double Divide(double a, double b);
};
}
Další třída definuje funkce obálky, které uživateli .NET umožňují vytvářet, odstraňovat a pracovat s podkladovou nativní MyMathFuncs
třídou.
#include "MyMathFuncs.h"
using namespace MathFuncs;
extern "C" {
MyMathFuncs* CreateMyMathFuncsClass();
void DisposeMyMathFuncsClass(MyMathFuncs* ptr);
double MyMathFuncsAdd(MyMathFuncs *ptr, double a, double b);
double MyMathFuncsSubtract(MyMathFuncs *ptr, double a, double b);
double MyMathFuncsMultiply(MyMathFuncs *ptr, double a, double b);
double MyMathFuncsDivide(MyMathFuncs *ptr, double a, double b);
}
Bude to tyto obálkové funkce, které se použijí na straně Xamarinu.
Zabalení nativní knihovny (fáze 2)
Tato fáze vyžaduje předkompilované knihovny popsané v předchozí části.
Vytvoření řešení sady Visual Studio
V Visual Studio pro Mac klikněte na Nový projekt (z úvodní stránky) nebo Na nové řešení (v nabídce Soubor).
V okně Nový projekt zvolte Sdílený projekt (v rámci multiplatformní > knihovny) a potom klepněte na tlačítko Další.
Aktualizujte následující pole a klikněte na vytvořit:
- Název projektu: MathFuncs.Shared
- Název řešení: MathFuncs
- Umístění: Použijte výchozí umístění pro uložení (nebo vyberte alternativu).
- Vytvoření projektu v adresáři řešení: Nastavte ho na zaškrtnuté.
V Průzkumník řešení poklikejte na projekt MathFuncs.Shared a přejděte na hlavní Nastavení.
Odebrat . Sdílí se z výchozího oboru názvů , takže je nastavena pouze na MathFuncs a potom klepněte na tlačítko OK.
Otevřete MyClass.cs (vytvořenou šablonou) a přejmenujte třídu i název souboru na MyMathFuncsWrapper a změňte obor názvů na MathFuncs.
CONTROL+ KLIKNĚTE na řešení MathFuncs a pak v nabídce Přidat zvolte Přidat nový projekt...
V okně Nový projekt zvolte knihovnu .NET Standard (z víceplatformní > knihovny) a potom klikněte na Tlačítko Další.
Zvolte .NET Standard 2.0 a potom klepněte na tlačítko Další.
Aktualizujte následující pole a klikněte na vytvořit:
- Název projektu: MathFuncs.Standard
- Umístění: Použijte stejné umístění pro uložení jako sdílený projekt.
V Průzkumník řešení poklikejte na projekt MathFuncs.Standard.
Přejděte na hlavní Nastavení a aktualizujte výchozí obor názvů na MathFuncs.
Přejděte do nastavení Výstup a aktualizujte název sestavení na MathFuncs.
Přejděte do nastavení kompilátoru , změňte konfiguraci na release, nastavte ladicí informace pouze na symboly a potom klepněte na tlačítko OK.
Odstraňte Class1.cs/Začínáme z projektu (pokud je některá z těchto součástí šablony).
CONTROL+ KLIKNĚTE na složku Závislosti/Odkazy projektu a pak zvolte Upravit odkazy.
Na kartě Projekty vyberte MathFuncs.Shareda potom klikněte na OK.
Opakujte kroky 7 až 17 (ignorování kroku 9) pomocí následujících konfigurací:
NÁZEV PROJEKTU NÁZEV ŠABLONY NABÍDKA NOVÝ PROJEKT MathFuncs.Android Knihovna tříd Knihovna pro Android > MathFuncs.iOS Knihovna vazeb Knihovna pro iOS > V Průzkumník řešení poklikejte na projekt MathFuncs.Android a pak přejděte do nastavení kompilátoru.
Pokud je konfigurace nastavená na Ladění, upravte Definovat symboly tak, aby zahrnovaly Android;
Změňte konfiguraci na Verzi a pak upravte Definovat symboly tak, aby zahrnovaly i Android;
Opakujte kroky 19–20 pro MathFuncs.iOS a upravte definice symbolů tak, aby zahrnovaly iOS, místo Androidu. V obou případech.
Sestavte řešení v konfiguraci vydané verze (CONTROL + COMMAND + B) a ověřte, že všechna tři výstupní sestavení (Android, iOS, .NET Standard) (v příslušných složkách přihrádek projektu) sdílejí stejný název MathFuncs.dll.
V této fázi by řešení mělo mít tři cíle, jeden kus pro Android, iOS a .NET Standard a sdílený projekt, na který odkazuje každý ze tří cílů. Ty by měly být nakonfigurované tak, aby používaly stejný výchozí obor názvů a výstupní sestavení se stejným názvem. To je nezbytné pro přístup "návnada a přepnutí", který jsme zmínili dříve.
Přidání nativních knihoven
Proces přidání nativních knihoven do řešení obálky se mírně liší mezi Androidem a iOSem.
Nativní reference pro MathFuncs.Android
CONTROL +CLICK v projektu MathFuncs.Android a pak zvolte Nová složka z nabídky Přidat název lib.
Pro každou ABI (Binární rozhraní aplikace), CONTROL + CLICK ve složce lib a pak zvolte Nová složka z nabídky Přidat a pojmete ji za příslušnou ABI. V tomto případě:
- arm64-v8a
- armeabi-v7a
- x86
- x86_64
Poznámka:
Podrobnější přehled najdete v tématu Architektury a procesory z příručky pro vývojáře NDK, konkrétně v části věnované adresování nativního kódu v balíčcích aplikací.
Ověřte strukturu složek:
- lib - arm64-v8a - armeabi-v7a - x86 - x86_64
Přidejte odpovídající knihovny .so do každé ze složek ABI na základě následujícího mapování:
arm64-v8a: lib/Android/arm64
armeabi-v7a: lib/Android/arm
x86: lib/Android/x86
x86_64: lib/Android/x86_64
Poznámka:
Chcete-li přidat soubory, CONTROL + KLIKNĚTE na složku představující odpovídající ABI a pak zvolte Přidat soubory... v nabídce Přidat . Zvolte příslušnou knihovnu (z adresáře PrecompiledLibs ) a potom klepněte na tlačítko Otevřít a potom klepněte na tlačítko OK a ponechte výchozí možnost Kopírovat soubor do adresáře.
Pro každý ze souborů .so , CONTROL + CLICK pak zvolte Možnost EmbeddedNativeLibrary z nabídky Akce sestavení.
Teď by se složka lib měla zobrazit takto:
- lib
- arm64-v8a
- libMathFuncs.so
- armeabi-v7a
- libMathFuncs.so
- x86
- libMathFuncs.so
- x86_64
- libMathFuncs.so
Nativní reference pro MathFuncs.iOS
CONTROL+CLICK v projektu MathFuncs.iOS a potom v nabídce Přidat zvolte Přidat nativní odkaz.
Zvolte knihovnu libMathFuncs.a (z knihovny libs/ios v adresáři PrecompiledLibs ) a potom klikněte na Otevřít.
CONTROL+CLICK v souboru libMathFuncs (ve složce Nativní odkazy a pak v nabídce zvolte možnost Vlastnosti
Nakonfigurujte vlastnosti nativního odkazu tak, aby byly zaškrtnuté (zobrazující ikonu zaškrtnutí) v oblasti Vlastnosti :
- Vynucení načtení
- Je C++
- Inteligentní odkaz
Poznámka:
Použití typu projektu vazby knihovny spolu s nativním odkazem vloží statickou knihovnu a umožňuje ji automaticky propojit s aplikací Xamarin.iOS, která na ni odkazuje (i když je součástí balíčku NuGet).
Otevřete ApiDefinition.cs, odstraňte kód s komentářem šablony (ponechte jenom
MathFuncs
obor názvů) a pak proveďte stejný krok pro Structs.csPoznámka:
Projekt knihovny vazeb vyžaduje tyto soubory (s akcemi sestavení ObjCBindingApiDefinition a ObjCBindingCoreSource ) k sestavení. Kód však napíšeme, abychom volali naši nativní knihovnu mimo tyto soubory způsobem, který lze sdílet mezi cíli knihovny Pro Android i iOS pomocí standardního volání nespravovaného kódu.
Psaní kódu spravované knihovny
Teď napište kód jazyka C#, který zavolá nativní knihovnu. Cílem je skrýt všechny základní složitosti. Příjemce by neměl potřebovat žádné pracovní znalosti o interních konceptech nativní knihovny ani konceptů volání ne.
Vytvoření Sejf Handle
CONTROL+ KLIKNĚTE na projekt MathFuncs.Shared a pak v nabídce Přidat zvolte Přidat soubor...
V okně Nový soubor zvolte prázdnou třídu, pojmenujte ji MyMathFuncs Sejf Handle a potom klikněte na tlačítko Nový.
Implementujte MyMathFuncs Sejf Handle třída:
using System; using Microsoft.Win32.SafeHandles; namespace MathFuncs { internal class MyMathFuncsSafeHandle : SafeHandleZeroOrMinusOneIsInvalid { public MyMathFuncsSafeHandle() : base(true) { } public IntPtr Ptr => handle; protected override bool ReleaseHandle() { // TODO: Release the handle here return true; } } }
Poznámka:
Upřednostňovaným způsobem práce s nespravovanými prostředky ve spravovaném kódu je Sejf Handle. Tím se abstrahuje spousta často používaného kódu souvisejícího s kritickou finalizací a životním cyklem objektů. Vlastník tohoto popisovače může následně zacházet jako s jakýmkoli jiným spravovaným prostředkem a nebude muset implementovat úplný model na jedno použití.
Vytvoření interní třídy obálky
Otevřete MyMathFuncsWrapper.cs a změňte ho na interní statickou třídu.
namespace MathFuncs { internal static class MyMathFuncsWrapper { } }
Do stejného souboru přidejte do třídy následující podmíněný příkaz:
#if Android const string DllName = "libMathFuncs.so"; #else const string DllName = "__Internal"; #endif
Poznámka:
Tím se nastaví konstantní hodnota DllName na základě toho, jestli je knihovna vytvořená pro Android nebo iOS. Jde o řešení různých konvencí pojmenování používaných každou příslušnou platformou, ale také typu knihovny, která se v tomto případě používá. Android používá dynamickou knihovnu, takže očekává název souboru včetně přípony. Pro iOS se vyžaduje __Internal, protože používáme statickou knihovnu.
Přidání odkazu na System.Runtime.InteropServices v horní části souboru MyMathFuncsWrapper.cs
using System.Runtime.InteropServices;
Přidejte metody obálky pro zpracování vytvoření a odstranění MyMathFuncs třídy:
[DllImport(DllName, EntryPoint = "CreateMyMathFuncsClass")] internal static extern MyMathFuncsSafeHandle CreateMyMathFuncs(); [DllImport(DllName, EntryPoint = "DisposeMyMathFuncsClass")] internal static extern void DisposeMyMathFuncs(MyMathFuncsSafeHandle ptr);
Poznámka:
Předáváme konstantu DllName atributu DllImport spolu s EntryPointem, který explicitně říká modulu runtime .NET název funkce, která má volat v této knihovně. Technicky vzato nemusíme zadávat hodnotu EntryPointu, pokud byly názvy spravovaných metod stejné jako nespravované. Pokud ho nezadáte, použije se místo toho název spravované metody jako EntryPoint . Je ale lepší být explicitní.
Přidejte metody obálky, které nám umožní pracovat s MyMathFuncs třídy pomocí následujícího kódu:
[DllImport(DllName, EntryPoint = "MyMathFuncsAdd")] internal static extern double Add(MyMathFuncsSafeHandle ptr, double a, double b); [DllImport(DllName, EntryPoint = "MyMathFuncsSubtract")] internal static extern double Subtract(MyMathFuncsSafeHandle ptr, double a, double b); [DllImport(DllName, EntryPoint = "MyMathFuncsMultiply")] internal static extern double Multiply(MyMathFuncsSafeHandle ptr, double a, double b); [DllImport(DllName, EntryPoint = "MyMathFuncsDivide")] internal static extern double Divide(MyMathFuncsSafeHandle ptr, double a, double b);
Poznámka:
Pro parametry v tomto příkladu používáme jednoduché typy. Vzhledem k tomu, že zařazování je bitové kopie v tomto případě nevyžaduje žádnou další práci na naší straně. Všimněte si také použití MyMathFuncs Sejf Handle třídy místo standardní IntPtr. IntPtr se automaticky mapuje na Sejf Handle v rámci procesu zařazování.
Ověřte, že dokončená třída MyMathFuncsWrapper vypadá takto:
using System.Runtime.InteropServices; namespace MathFuncs { internal static class MyMathFuncsWrapper { #if Android const string DllName = "libMathFuncs.so"; #else const string DllName = "__Internal"; #endif [DllImport(DllName, EntryPoint = "CreateMyMathFuncsClass")] internal static extern MyMathFuncsSafeHandle CreateMyMathFuncs(); [DllImport(DllName, EntryPoint = "DisposeMyMathFuncsClass")] internal static extern void DisposeMyMathFuncs(MyMathFuncsSafeHandle ptr); [DllImport(DllName, EntryPoint = "MyMathFuncsAdd")] internal static extern double Add(MyMathFuncsSafeHandle ptr, double a, double b); [DllImport(DllName, EntryPoint = "MyMathFuncsSubtract")] internal static extern double Subtract(MyMathFuncsSafeHandle ptr, double a, double b); [DllImport(DllName, EntryPoint = "MyMathFuncsMultiply")] internal static extern double Multiply(MyMathFuncsSafeHandle ptr, double a, double b); [DllImport(DllName, EntryPoint = "MyMathFuncsDivide")] internal static extern double Divide(MyMathFuncsSafeHandle ptr, double a, double b); } }
Dokončení Třídy MyMathFuncs Sejf Handle
Otevřete MyMathFuncs Sejf Handle třída, přejděte do zástupného komentáře TODO v metodě ReleaseHandle:
// TODO: Release the handle here
Nahraďte řádek TODO:
MyMathFuncsWrapper.DisposeMyMathFuncs(this);
Psaní třídy MyMathFuncs
Teď, když je obálka dokončena, vytvořte MyMathFuncs třídy, která bude spravovat odkaz na nespravovaný objekt C++ MyMathFuncs.
CONTROL+ KLIKNĚTE na projekt MathFuncs.Shared a pak v nabídce Přidat zvolte Přidat soubor...
V okně Nový soubor zvolte Prázdnou třídu, pojmenujte ji MyMathFuncs a potom klikněte na Tlačítko Nový.
Do třídy MyMathFuncs přidejte následující členy:
readonly MyMathFuncsSafeHandle handle;
Implementujte konstruktor pro třídu, takže vytvoří a uloží popisovač nativní MyMathFuncs objekt při vytvoření instance třídy:
public MyMathFuncs() { handle = MyMathFuncsWrapper.CreateMyMathFuncs(); }
Implementujte rozhraní IDisposable pomocí následujícího kódu:
public class MyMathFuncs : IDisposable { ... protected virtual void Dispose(bool disposing) { if (handle != null && !handle.IsInvalid) handle.Dispose(); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } // ... }
Implementujte MyMathFuncs metody pomocí MyMathFuncsWrapper třídy provést skutečnou práci pod kapotou předáním ukazatele, který jsme uložili do základního nespravovaného objektu. Kód by měl být následující:
public double Add(double a, double b) { return MyMathFuncsWrapper.Add(handle, a, b); } public double Subtract(double a, double b) { return MyMathFuncsWrapper.Subtract(handle, a, b); } public double Multiply(double a, double b) { return MyMathFuncsWrapper.Multiply(handle, a, b); } public double Divide(double a, double b) { return MyMathFuncsWrapper.Divide(handle, a, b); }
Vytvoření nuspec
Aby bylo možné knihovnu zabalit a distribuovat prostřednictvím NuGetu, potřebuje řešení soubor nuspec . Tím určíte, která z výsledných sestavení budou zahrnuta pro každou podporovanou platformu.
CONTROL +CLICK na řešení MathFuncs a pak zvolte Přidat složku řešení z nabídky Přidat název SolutionItems.
CONTROL +CLICK ve složce SolutionItems a pak v nabídce Přidat zvolte Nový soubor...
V okně Nový soubor zvolte Prázdný soubor XML, pojmenujte jej MathFuncs.nuspec a klikněte na tlačítko Nový.
Aktualizujte MathFuncs.nuspec se základními metadaty balíčku, která se mají zobrazit příjemci NuGetu . Příklad:
<?xml version="1.0"?> <package> <metadata> <id>MathFuncs</id> <version>$version$</version> <authors>Microsoft Mobile Customer Advisory Team</authors> <description>Sample C++ Wrapper Library</description> <requireLicenseAcceptance>false</requireLicenseAcceptance> <copyright>Copyright 2018</copyright> </metadata> </package>
<files>
Přidejte prvek jako podřízený<package>
prvek (těsně pod<metadata>
), identifikujte každý soubor samostatným<file>
prvkem:<files> <!-- Android --> <!-- iOS --> <!-- netstandard2.0 --> </files>
Poznámka:
Když je balíček nainstalován do projektu a kde existuje více sestavení určených stejným názvem, NuGet efektivně zvolí sestavení, které je nejvíce specifické pro danou platformu.
<file>
Přidejte prvky pro sestavení Androidu:<file src="MathFuncs.Android/bin/Release/MathFuncs.dll" target="lib/MonoAndroid81/MathFuncs.dll" /> <file src="MathFuncs.Android/bin/Release/MathFuncs.pdb" target="lib/MonoAndroid81/MathFuncs.pdb" />
<file>
Přidejte prvky pro sestavení iOS:<file src="MathFuncs.iOS/bin/Release/MathFuncs.dll" target="lib/Xamarin.iOS10/MathFuncs.dll" /> <file src="MathFuncs.iOS/bin/Release/MathFuncs.pdb" target="lib/Xamarin.iOS10/MathFuncs.pdb" />
<file>
Přidejte prvky pro sestavení netstandard2.0:<file src="MathFuncs.Standard/bin/Release/netstandard2.0/MathFuncs.dll" target="lib/netstandard2.0/MathFuncs.dll" /> <file src="MathFuncs.Standard/bin/Release/netstandard2.0/MathFuncs.pdb" target="lib/netstandard2.0/MathFuncs.pdb" />
Ověřte manifest nuspec:
<?xml version="1.0"?> <package> <metadata> <id>MathFuncs</id> <version>$version$</version> <authors>Microsoft Mobile Customer Advisory Team</authors> <description>Sample C++ Wrapper Library</description> <requireLicenseAcceptance>false</requireLicenseAcceptance> <copyright>Copyright 2018</copyright> </metadata> <files> <!-- Android --> <file src="MathFuncs.Android/bin/Release/MathFuncs.dll" target="lib/MonoAndroid81/MathFuncs.dll" /> <file src="MathFuncs.Android/bin/Release/MathFuncs.pdb" target="lib/MonoAndroid81/MathFuncs.pdb" /> <!-- iOS --> <file src="MathFuncs.iOS/bin/Release/MathFuncs.dll" target="lib/Xamarin.iOS10/MathFuncs.dll" /> <file src="MathFuncs.iOS/bin/Release/MathFuncs.pdb" target="lib/Xamarin.iOS10/MathFuncs.pdb" /> <!-- netstandard2.0 --> <file src="MathFuncs.Standard/bin/Release/netstandard2.0/MathFuncs.dll" target="lib/netstandard2.0/MathFuncs.dll" /> <file src="MathFuncs.Standard/bin/Release/netstandard2.0/MathFuncs.pdb" target="lib/netstandard2.0/MathFuncs.pdb" /> </files> </package>
Poznámka:
Tento soubor určuje výstupní cesty sestavení z sestavení vydané verze , proto nezapomeňte sestavit řešení pomocí této konfigurace.
V tomto okamžiku řešení obsahuje 3 sestavení .NET a podpůrný manifest nuspec .
Distribuce obálky .NET pomocí NuGetu
Dalším krokem je zabalení a distribuce balíčku NuGet, aby ho mohla aplikace snadno využívat a spravovat jako závislost. Zabalení a spotřeba by mohly být provedeny v rámci jednoho řešení, ale distribuce knihovny prostřednictvím NuGet pomáhá při oddělení a umožňuje nám nezávisle spravovat tyto základy kódu.
Příprava místního adresáře balíčků
Nejjednodušší forma informačního kanálu NuGet je místní adresář:
- Ve Finderu přejděte do vhodného adresáře. Například /Users.
- V nabídce Soubor zvolte Možnost Nová složkaa zadejte smysluplný název, například local-nuget-feed.
Vytvoření balíčku
Nastavte konfiguraci sestavení na verzi a spusťte sestavení pomocí COMMAND + B.
Otevřete Terminál a změňte adresář na složku obsahující soubor nuspec .
V terminálu spusťte příkaz nuget pack určující soubor nuspec , verzi (například 1.0.0) a outputDirectory pomocí složky vytvořené v předchozím kroku, tj. local-nuget-feed. Příklad:
nuget pack MathFuncs.nuspec -Version 1.0.0 -OutputDirectory ~/local-nuget-feed
Ověřte, že byl v adresáři local-nuget-feed vytvořen MathFuncs.1.0.0.nupkg.
[VOLITELNÉ] Použití privátního informačního kanálu NuGet s Azure DevOps
Robustnější technika je popsaná v tématu Začínáme s balíčky NuGet v Azure DevOps, která ukazuje, jak vytvořit privátní kanál a odeslat balíček (vygenerovaný v předchozím kroku) do daného informačního kanálu.
Je ideální mít tento pracovní postup plně automatizovaný, například pomocí Azure Pipelines. Další informace najdete v tématu Začínáme se službou Azure Pipelines.
Využívání obálky .NET z aplikace Xamarin.Forms
Chcete-li dokončit názorný postup, vytvořte aplikaci Xamarin.Forms , která bude využívat balíček právě publikovaný v místním informačním kanálu NuGet .
Vytvoření projektu Xamarin.Forms
Otevřete novou instanci Visual Studio pro Mac. Můžete to udělat z terminálu:
open -n -a "Visual Studio"
V Visual Studio pro Mac klikněte na Nový projekt (z úvodní stránky) nebo Na nové řešení (v nabídce Soubor).
V okně Nový projekt zvolte Prázdnou aplikaci Forms (z víceplatformové > aplikace) a potom klikněte na Další.
Aktualizujte následující pole a klepněte na tlačítko Další:
- Název aplikace: MathFuncsApp.
- Identifikátor organizace: Použijte reverzní obor názvů, například com.{your_org}.
- Cílové platformy: Použijte výchozí (cíle Pro Android i iOS).
- Sdílený kód: Nastavte ho na .NET Standard (řešení "Sdílená knihovna" je možné, ale nad rámec tohoto názorného postupu).
Aktualizujte následující pole a klikněte na vytvořit:
- Název projektu: MathFuncsApp.
- Název řešení: MathFuncsApp.
- Umístění: Použijte výchozí umístění pro uložení (nebo vyberte alternativu).
V Průzkumník řešení stiskněte control + kliknutí na cíl (MathFuncsApp.Android nebo MathFuncs.iOS) pro počáteční testování a pak zvolte Nastavit jako spouštěný projekt.
Zvolte upřednostňované zařízení nebo emulátor simulátoru/.
Spuštěním řešení (COMMAND + RETURN) ověřte, že se šablona projektu Xamarin.Forms sestaví a spustí v pořádku.
Poznámka:
iOS (konkrétně simulátor) má tendenci mít nejrychlejší čas sestavení a nasazení.
Přidání místního informačního kanálu NuGet do konfigurace NuGet
V sadě Visual Studio zvolte Předvolby (v nabídce sady Visual Studio ).
V části NuGet zvolte Zdroje a pak klikněte na Přidat.
Aktualizujte následující pole a klikněte na přidat zdroj:
- Název: Zadejte smysluplný název, například Local-Packages.
- Umístění: Zadejte složku local-nuget-feed vytvořenou v předchozím kroku.
Poznámka:
V takovém případě není nutné zadávat uživatelské jméno a heslo.
Klikněte na OK.
Odkazování na balíček
Opakujte následující kroky pro každý projekt (MathFuncsApp, MathFuncsApp.Android a MathFuncsApp.iOS).
- CONTROL+ KLIKNĚTE na projekt a pak v nabídce Přidat zvolte Přidat balíčky NuGet.
- Vyhledejte MathFuncs.
- Ověřte, že verze balíčku je 1.0.0 a další podrobnosti se zobrazí podle očekávání, například název a popis, tj. MathFuncs a ukázková knihovna obálky C++.
- Vyberte balíček MathFuncs a potom klikněte na Přidat balíček.
Použití funkcí knihovny
Teď, s odkazem na balíček MathFuncs v každém z projektů, jsou funkce k dispozici pro kód jazyka C#.
Otevřete MainPage.xaml.cs v rámci společného projektu Xamarin.Formspro MathFuncsApp (na který odkazuje MathFuncsApp.Android i MathFuncsApp.iOS).
V horní části souboru přidejte příkazy using pro System.Diagnostics a MathFuncs :
using System.Diagnostics; using MathFuncs;
Deklarujte instanci
MyMathFuncs
třídy v horní částiMainPage
třídy:MyMathFuncs myMathFuncs;
Přepište metody
OnAppearing
zeOnDisappearing
ContentPage
základní třídy:protected override void OnAppearing() { base.OnAppearing(); } protected override void OnDisappearing() { base.OnDisappearing(); }
Aktualizujte metodu
OnAppearing
myMathFuncs
tak, aby inicializovala proměnnou deklarovanou dříve:protected override void OnAppearing() { base.OnAppearing(); myMathFuncs = new MyMathFuncs(); }
Aktualizujte metodu
OnDisappearing
tak, aby volala metodu:Dispose
myMathFuncs
protected override void OnDisappearing() { base.OnAppearing(); myMathFuncs.Dispose(); }
Implementujte privátní metodu s názvem TestMathFuncs následujícím způsobem:
private void TestMathFuncs() { var numberA = 1; var numberB = 2; // Test Add function var addResult = myMathFuncs.Add(numberA, numberB); // Test Subtract function var subtractResult = myMathFuncs.Subtract(numberA, numberB); // Test Multiply function var multiplyResult = myMathFuncs.Multiply(numberA, numberB); // Test Divide function var divideResult = myMathFuncs.Divide(numberA, numberB); // Output results Debug.WriteLine($"{numberA} + {numberB} = {addResult}"); Debug.WriteLine($"{numberA} - {numberB} = {subtractResult}"); Debug.WriteLine($"{numberA} * {numberB} = {multiplyResult}"); Debug.WriteLine($"{numberA} / {numberB} = {divideResult}"); }
Nakonec zavolejte
TestMathFuncs
na konciOnAppearing
metody:TestMathFuncs();
Spusťte aplikaci na každé cílové platformě a ověřte výstup v oblasti výstupu aplikace následujícím způsobem:
1 + 2 = 3 1 - 2 = -1 1 * 2 = 2 1 / 2 = 0.5
Poznámka:
Pokud při testování na Androidu nebo chyby sestavení v iOSu narazíte na knihovnu DLLNotFoundException, nezapomeňte zkontrolovat, jestli je architektura procesoru zařízení/emulátoru/simulátoru, kterou používáte, kompatibilní s podmnožinou, kterou jsme zvolili pro podporu.
Shrnutí
Tento článek vysvětluje, jak vytvořit aplikaci Xamarin.Forms, která používá nativní knihovny prostřednictvím společné obálky .NET distribuované prostřednictvím balíčku NuGet. Příklad uvedený v tomto návodu je záměrně velmi zjednodušující, aby se snadněji ukázal přístup. Skutečná aplikace bude muset řešit složitosti, jako je zpracování výjimek, zpětné volání, zařazování složitějších typů a propojení s jinými knihovnami závislostí. Klíčovým aspektem je proces, při kterém je vývoj kódu C++ koordinovaný a synchronizovaný s obálkou a klientskými aplikacemi. Tento proces se může lišit v závislosti na tom, jestli jeden nebo oba tyto obavy jsou zodpovědností jednoho týmu. V obou směrech je automatizace skutečnou výhodou. Níže jsou uvedeny některé zdroje informací, které poskytují další informace o některých klíčových konceptech spolu s příslušnými soubory ke stažení.