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.

High-level approach for using C/C++ with Xamarin

4 fáze jsou:

  1. Kompilace zdrojového kódu C/C++ do nativních knihoven specifických pro platformu.
  2. Zabalení nativních knihoven pomocí řešení sady Visual Studio
  3. Balení a nabízení balíčku NuGet pro obálku .NET
  4. 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

  1. V Visual Studio pro Mac klikněte na Nový projekt (z úvodní stránky) nebo Na nové řešení (v nabídce Soubor).

  2. V okně Nový projekt zvolte Sdílený projekt (v rámci multiplatformní > knihovny) a potom klepněte na tlačítko Další.

  3. 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é.
  4. V Průzkumník řešení poklikejte na projekt MathFuncs.Shared a přejděte na hlavní Nastavení.

  5. 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.

  6. 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.

  7. CONTROL+ KLIKNĚTE na řešení MathFuncs a pak v nabídce Přidat zvolte Přidat nový projekt...

  8. V okně Nový projekt zvolte knihovnu .NET Standard (z víceplatformní > knihovny) a potom klikněte na Tlačítko Další.

  9. Zvolte .NET Standard 2.0 a potom klepněte na tlačítko Další.

  10. 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.
  11. V Průzkumník řešení poklikejte na projekt MathFuncs.Standard.

  12. Přejděte na hlavní Nastavení a aktualizujte výchozí obor názvů na MathFuncs.

  13. Přejděte do nastavení Výstup a aktualizujte název sestavení na MathFuncs.

  14. 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.

  15. Odstraňte Class1.cs/Začínáme z projektu (pokud je některá z těchto součástí šablony).

  16. CONTROL+ KLIKNĚTE na složku Závislosti/Odkazy projektu a pak zvolte Upravit odkazy.

  17. Na kartě Projekty vyberte MathFuncs.Shareda potom klikněte na OK.

  18. 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 >
  19. V Průzkumník řešení poklikejte na projekt MathFuncs.Android a pak přejděte do nastavení kompilátoru.

  20. Pokud je konfigurace nastavená na Ladění, upravte Definovat symboly tak, aby zahrnovaly Android;

  21. Změňte konfiguraci na Verzi a pak upravte Definovat symboly tak, aby zahrnovaly i Android;

  22. Opakujte kroky 19–20 pro MathFuncs.iOS a upravte definice symbolů tak, aby zahrnovaly iOS, místo Androidu. V obou případech.

  23. 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

  1. CONTROL +CLICK v projektu MathFuncs.Android a pak zvolte Nová složka z nabídky Přidat název lib.

  2. 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í.

  3. Ověřte strukturu složek:

    - lib
        - arm64-v8a
        - armeabi-v7a
        - x86
        - x86_64
    
  4. 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.

  5. 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

  1. CONTROL+CLICK v projektu MathFuncs.iOS a potom v nabídce Přidat zvolte Přidat nativní odkaz.

  2. Zvolte knihovnu libMathFuncs.a (z knihovny libs/ios v adresáři PrecompiledLibs ) a potom klikněte na Otevřít.

  3. CONTROL+CLICK v souboru libMathFuncs (ve složce Nativní odkazy a pak v nabídce zvolte možnost Vlastnosti

  4. 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).

  5. 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.cs

    Pozná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

  1. CONTROL+ KLIKNĚTE na projekt MathFuncs.Shared a pak v nabídce Přidat zvolte Přidat soubor...

  2. V okně Nový soubor zvolte prázdnou třídu, pojmenujte ji MyMathFuncs Sejf Handle a potom klikněte na tlačítko Nový.

  3. 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

  1. Otevřete MyMathFuncsWrapper.cs a změňte ho na interní statickou třídu.

    namespace MathFuncs
    {
        internal static class MyMathFuncsWrapper
        {
        }
    }
    
  2. 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.

  3. Přidání odkazu na System.Runtime.InteropServices v horní části souboru MyMathFuncsWrapper.cs

    using System.Runtime.InteropServices;
    
  4. 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í.

  5. 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í.

  6. 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

  1. 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
    
  2. 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.

  1. CONTROL+ KLIKNĚTE na projekt MathFuncs.Shared a pak v nabídce Přidat zvolte Přidat soubor...

  2. V okně Nový soubor zvolte Prázdnou třídu, pojmenujte ji MyMathFuncs a potom klikněte na Tlačítko Nový.

  3. Do třídy MyMathFuncs přidejte následující členy:

    readonly MyMathFuncsSafeHandle handle;
    
  4. 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();
    }
    
  5. 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);
        }
    
        // ...
    }
    
  6. 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.

  1. CONTROL +CLICK na řešení MathFuncs a pak zvolte Přidat složku řešení z nabídky Přidat název SolutionItems.

  2. CONTROL +CLICK ve složce SolutionItems a pak v nabídce Přidat zvolte Nový soubor...

  3. V okně Nový soubor zvolte Prázdný soubor XML, pojmenujte jej MathFuncs.nuspec a klikněte na tlačítko Nový.

  4. 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>
    

    Poznámka:

    Další podrobnosti o schématu použitém pro tento manifest najdete v referenčním dokumentu nuspec.

  5. <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.

  6. <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" />
    
  7. <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" />
    
  8. <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" />
    
  9. 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ář:

  1. Ve Finderu přejděte do vhodného adresáře. Například /Users.
  2. V nabídce Soubor zvolte Možnost Nová složkaa zadejte smysluplný název, například local-nuget-feed.

Vytvoření balíčku

  1. Nastavte konfiguraci sestavení na verzi a spusťte sestavení pomocí COMMAND + B.

  2. Otevřete Terminál a změňte adresář na složku obsahující soubor nuspec .

  3. 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
    
  4. 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

  1. Otevřete novou instanci Visual Studio pro Mac. Můžete to udělat z terminálu:

    open -n -a "Visual Studio"
    
  2. V Visual Studio pro Mac klikněte na Nový projekt (z úvodní stránky) nebo Na nové řešení (v nabídce Soubor).

  3. V okně Nový projekt zvolte Prázdnou aplikaci Forms (z víceplatformové > aplikace) a potom klikněte na Další.

  4. 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).
  5. 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).
  6. 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.

  7. Zvolte upřednostňované zařízení nebo emulátor simulátoru/.

  8. 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

  1. V sadě Visual Studio zvolte Předvolby (v nabídce sady Visual Studio ).

  2. V části NuGet zvolte Zdroje a pak klikněte na Přidat.

  3. 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.

  4. Klikněte na OK.

Odkazování na balíček

Opakujte následující kroky pro každý projekt (MathFuncsApp, MathFuncsApp.Android a MathFuncsApp.iOS).

  1. CONTROL+ KLIKNĚTE na projekt a pak v nabídce Přidat zvolte Přidat balíčky NuGet.
  2. Vyhledejte MathFuncs.
  3. 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++.
  4. 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#.

  1. Otevřete MainPage.xaml.cs v rámci společného projektu Xamarin.Formspro MathFuncsApp (na který odkazuje MathFuncsApp.Android i MathFuncsApp.iOS).

  2. V horní části souboru přidejte příkazy using pro System.Diagnostics a MathFuncs :

    using System.Diagnostics;
    using MathFuncs;
    
  3. Deklarujte instanci MyMathFuncs třídy v horní části MainPage třídy:

    MyMathFuncs myMathFuncs;
    
  4. Přepište metody OnAppearing ze OnDisappearingContentPage základní třídy:

    protected override void OnAppearing()
    {
        base.OnAppearing();
    }
    
    protected override void OnDisappearing()
    {
        base.OnDisappearing();
    }
    
  5. Aktualizujte metodu OnAppearingmyMathFuncs tak, aby inicializovala proměnnou deklarovanou dříve:

    protected override void OnAppearing()
    {
        base.OnAppearing();
        myMathFuncs = new MyMathFuncs();
    }
    
  6. Aktualizujte metodu OnDisappearing tak, aby volala metodu:DisposemyMathFuncs

    protected override void OnDisappearing()
    {
        base.OnAppearing();
        myMathFuncs.Dispose();
    }
    
  7. 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}");
    }
    
  8. Nakonec zavolejte TestMathFuncs na konci OnAppearing metody:

    TestMathFuncs();
    
  9. 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í.

Soubory ke stažení

Příklady

Další čtení

Články týkající se obsahu tohoto příspěvku