C/C++ kitaplıklarını Xamarin ile kullanma
Genel bakış
Xamarin, geliştiricilerin Visual Studio ile platformlar arası yerel mobil uygulamalar oluşturmasını sağlar. C# bağlamaları genellikle mevcut platform bileşenlerini geliştiricilere göstermek için kullanılır. Ancak, Xamarin uygulamalarının mevcut kod temelleriyle çalışması gereken zamanlar vardır. Bazen ekiplerin büyük, iyi test edilmiş ve yüksek oranda iyileştirilmiş bir kod tabanını C# ile taşıması için yeterli zamanı, bütçeyi veya kaynakları yoktur.
Platformlar arası mobil geliştirme için Visual C++, C/C++ ve C# kodunun aynı çözümün bir parçası olarak derlenmesini sağlar ve birleşik hata ayıklama deneyimi de dahil olmak üzere birçok avantaj sunar. Microsoft, Hyperlapse Mobile ve Pix Kamera gibi uygulamaları sunmak için C/C++ ve Xamarin'i bu şekilde kullanmıştır.
Ancak bazı durumlarda, mevcut C/C++ araçlarını ve işlemlerini yerinde tutma ve kitaplık kodunu uygulamadan ayrı tutma ve kitaplığı üçüncü taraf bir bileşene benzermiş gibi ele alma isteği (veya gereksinimi) vardır. Bu gibi durumlarda, zorluk yalnızca ilgili üyeleri C# ile açığa çıkarmakla kalmaz, aynı zamanda kitaplığı bağımlılık olarak yönetmektir. Ve tabii ki, bu sürecin mümkün olduğunca çoğunu otomatikleştirmek.
Bu gönderide bu senaryo için üst düzey bir yaklaşım özetlenmiştir ve basit bir örnekte yol gösterilir.
Background
C/C++ platformlar arası bir dil olarak kabul edilir, ancak kaynak kodun yalnızca tüm hedef derleyiciler tarafından desteklenen ve koşullu olarak dahil edilen platform veya derleyiciye özgü kodun az veya hiç içermediği C/C++ kullanılarak platformlar arası olduğundan emin olmak için büyük özen gösterilmelidir.
Sonuçta kod tüm hedef platformlarda başarıyla derlenmeli ve çalıştırılmalıdır, bu nedenle bu hedeflenen platformlar (ve derleyiciler) genelindeki ortaklığa iner. Sorunlar hala derleyiciler arasındaki küçük farklardan kaynaklanabilir ve bu nedenle her bir hedef platformda kapsamlı test (tercihen otomatikleştirilmiş) giderek daha önemli hale gelir.
Üst düzey yaklaşım
Aşağıdaki çizim, C/C++ kaynak kodunu NuGet aracılığıyla paylaşılan ve ardından bir Xamarin.Forms uygulamasında kullanılan platformlar arası bir Xamarin kitaplığına dönüştürmek için kullanılan dört aşamalı yaklaşımı temsil eder.
4 aşama şunlardır:
- C/C++ kaynak kodunu platforma özgü yerel kitaplıklara derleme.
- Yerel kitaplıkları visual studio çözümüyle sarmalama.
- .NET sarmalayıcısı için bir NuGet paketini paketleme ve gönderme.
- Xamarin uygulamasından NuGet paketini kullanma.
1. Aşama: C/C++ kaynak kodunu platforma özgü yerel kitaplıklara derleme
Bu aşamanın amacı, C# sarmalayıcısı tarafından çağrılabilen yerel kitaplıklar oluşturmaktır. Bu durum, durumunuzla ilgili olabilir veya olmayabilir. Bu ortak senaryoda ele alınabilecek birçok araç ve işlem bu makalenin kapsamının dışındadır. Önemli noktalar C/C++ kod tabanını herhangi bir yerel sarmalayıcı kodu, yeterli birim testi ve derleme otomasyonu ile eşitlenmiş durumda tutmaktır.
Kılavuzdaki kitaplıklar, birlikte gelen bir kabuk betiğiyle Visual Studio Code kullanılarak oluşturulmuştur. Bu kılavuzun genişletilmiş bir sürümü, örneğin bu bölümünü daha ayrıntılı olarak ele alan Mobile CAT GitHub deposunda bulunabilir. Bu durumda yerel kitaplıklar üçüncü taraf bağımlılığı olarak ele alınmaktadır ancak bu aşama bağlam için gösterilmiştir.
Kolaylık olması için, izlenecek yol mimarilerin yalnızca bir alt kümesini hedefler. iOS için, bireysel mimariye özgü ikili dosyalardan tek bir yağ ikili dosyası oluşturmak için lipo yardımcı programını kullanır. Android, .so uzantılı dinamik ikili dosyaları, iOS ise .a uzantısına sahip statik bir yağ ikili dosyası kullanır.
2. Aşama: Yerel kitaplıkları Visual Studio çözümüyle sarmalama
Sonraki aşama, yerel kitaplıkları .NET'ten kolayca kullanılacak şekilde sarmalamadır. Bu işlem, dört proje içeren bir Visual Studio çözümüyle gerçekleştirilir. Paylaşılan proje ortak kodu içerir. Xamarin.Android, Xamarin.iOS ve .NET Standard'ın her birini hedefleyen projeler, kitaplığa platformdan bağımsız bir şekilde başvurulmasına olanak tanır.
Sarmalayıcı 'yem ve anahtar numarası' kullanır. Tek yol bu değildir, ancak kitaplığa başvurmayı kolaylaştırır ve kullanan uygulamanın kendi içinde platforma özgü uygulamaları açıkça yönetme gereksinimini ortadan kaldırır. İşin püf noktası, hedeflerin (.NET Standard, Android, iOS) aynı ad alanını, derleme adını ve sınıf yapısını paylaşmasını sağlamaktır. NuGet her zaman platforma özgü bir kitaplığı tercih edeceğinden, .NET Standard sürümü hiçbir zaman çalışma zamanında kullanılmaz.
Bu adımdaki çalışmaların çoğu, yerel kitaplık yöntemlerini çağırmak ve temel alınan nesnelere yapılan başvuruları yönetmek için P/Invoke kullanmaya odaklanacaktır. Amaç, kitaplığın işlevselliğini tüketiciye sunarken herhangi bir karmaşıklığı soyutlamadır. Xamarin.Forms geliştiricilerinin yönetilmeyen kitaplığın iç çalışmalarıyla ilgili çalışma bilgilerine sahip olması gerekmez. Başka bir yönetilen C# kitaplığı kullanıyormuş gibi hissetmelidir.
Sonuç olarak, bu aşamanın çıktısı bir sonraki adımda paketi oluşturmak için gereken bilgileri içeren nuspec belgesinin yanı sıra hedef başına bir .NET kitaplığı kümesidir.
3. Aşama: .NET sarmalayıcısı için bir NuGet paketini paketleme ve gönderme
Üçüncü aşama, önceki adımdaki derleme yapıtlarını kullanarak bir NuGet paketi oluşturmaktır. Bu adımın sonucu, Xamarin uygulamasından tüketilebilen bir NuGet paketidir. İzlenecek yol, NuGet akışı olarak hizmet vermek için yerel bir dizin kullanır. Üretimde, bu adım bir paketi genel veya özel bir NuGet akışına yayımlamalı ve tam olarak otomatik olmalıdır.
4. Aşama: Xamarin.Forms uygulamasından NuGet paketini kullanma
Son adım, Xamarin.Forms uygulamasından NuGet paketine başvurmak ve kullanmaktır. Bunun için Visual Studio'da NuGet akışının önceki adımda tanımlanan akışı kullanacak şekilde yapılandırılması gerekir.
Akış yapılandırıldıktan sonra, pakete platformlar arası Xamarin.Forms uygulamasındaki her projeden başvuru yapılması gerekir. 'Yem ve anahtar numarası' aynı arabirimler sağlar, bu nedenle yerel kitaplık işlevi tek bir konumda tanımlanan kod kullanılarak çağrılabilir.
Kaynak kod deposu, Azure DevOps'ta özel NuGet akışı ayarlama ve paketin bu akışa nasıl gönderildiğini gösteren makaleleri içeren daha fazla okuma listesini içerir. Yerel dizinden biraz daha fazla kurulum süresi gerektirse de, ekip geliştirme ortamında bu tür bir akış daha iyidir.
Gözden geçirme
Sağlanan adımlar Mac için Visual Studio özeldir, ancak yapı Visual Studio 2017'de de çalışır.
Önkoşullar
Bunu takip etmek için geliştiricinin şunları ihtiyacı vardır:
Not
Uygulamaları i Telefon dağıtmak için etkin bir Apple Geliştirici Hesabı gereklidir.
Yerel kitaplıkları oluşturma (1. Aşama)
Yerel kitaplık işlevselliği, İzlenecek Yol: Statik Kitaplık Oluşturma ve Kullanma (C++) örneğini temel alır.
Bu kılavuzda, kitaplık bu senaryoda üçüncü taraf bağımlılığı olarak sağlandığından, yerel kitaplıkları oluşturan ilk aşama atlanır. Önceden derlenmiş yerel kitaplıklar örnek koda eklenmiştir veya doğrudan indirilebilir.
Yerel kitaplıkla çalışma
Özgün MathFuncsLib örneği, aşağıdaki tanım ile adlı MyMathFuncs
tek bir sınıf içerir:
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);
};
}
Ek bir sınıf, .NET tüketicisinin temel alınan yerel MyMathFuncs
sınıfı oluşturmasına, atmasına ve bunlarla etkileşim kurmasına olanak sağlayan sarmalayıcı işlevlerini tanımlar.
#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);
}
Bu, Xamarin tarafında kullanılan sarmalayıcı işlevleri olacaktır.
Yerel kitaplığı sarmalama (2. Aşama)
Bu aşama, önceki bölümde açıklanan önceden derlenmiş kitaplıkları gerektirir.
Visual Studio çözümü oluşturma
Mac için Visual Studio'da, Yeni Proje'ye (Hoş Geldiniz Sayfasından) veya Yeni Çözüm'e (Dosya menüsünden) tıklayın.
Yeni Proje penceresinde Paylaşılan Proje'yi seçin (Çok Platformlu > Kitaplık'ın içinden) ve ardından İleri'ye tıklayın.
Aşağıdaki alanları güncelleştirin ve Oluştur'a tıklayın:
- Proje Adı: MathFuncs.Shared
- Çözüm Adı: MathFuncs
- Konum: Varsayılan kaydetme konumunu kullanın (veya alternatif bir konum seçin)
- Çözüm dizininde proje oluşturma: Bunu işaretli olarak ayarlayın
Çözüm Gezgini MathFuncs.Shared projesine çift tıklayın ve Ana Ayarlar gidin.
öğesini kaldırın. Varsayılan Ad Alanı'ndan paylaşıldığındanyalnızca MathFuncs olarak ayarlanır ve Tamam'a tıklayın.
MyClass.cs açın (şablon tarafından oluşturulur), ardından hem sınıfı hem de dosya adını MyMathFuncsWrapper olarak yeniden adlandırın ve ad alanını MathFuncs olarak değiştirin.
CONTROL + MathFuncs çözümüne tıklayın, ardından Ekle menüsünden Yeni Proje Ekle... öğesini seçin.
Yeni Proje penceresinde .NET Standart Kitaplığı'nı (Çok Platformlu > Kitaplık'ın içinden) seçin ve İleri'ye tıklayın.
.NET Standard 2.0'ı seçin ve İleri'ye tıklayın.
Aşağıdaki alanları güncelleştirin ve Oluştur'a tıklayın:
- Proje Adı: MathFuncs.Standard
- Konum: Paylaşılan projeyle aynı kaydetme konumunu kullanın
Çözüm Gezgini MathFuncs.Standard projesine çift tıklayın.
Ana Ayarlar gidin, ardından Varsayılan Ad Alanını MathFuncs olarak güncelleştirin.
Çıkış ayarlarına gidin, ardından Derleme adını MathFuncs olarak güncelleştirin.
Derleyici ayarlarına gidin, Yapılandırma'yı Yayın olarak değiştirin ve Hata ayıklama bilgilerini Yalnızca Simgeler olarak ayarlayın ve Tamam'a tıklayın.
projeden Class1.cs/Başlarken'i silin (bunlardan biri şablonun parçası olarak eklenmişse).
CONTROL + Proje Bağımlılıkları/Başvurular klasörüne tıklayın ve ardından Başvuruları Düzenle'yi seçin.
Projeler sekmesinde MathFuncs.Shared öğesini seçin ve tamam'a tıklayın.
Aşağıdaki yapılandırmaları kullanarak 7-17 arası adımları (9. adımı yoksayarak) yineleyin:
PROJE ADI ŞABLON ADı YENI PROJE MENÜSÜ MathFuncs.Android Sınıf Kitaplığı Android > Kitaplığı MathFuncs.iOS Bağlama Kitaplığı iOS > Kitaplığı Çözüm Gezgini MathFuncs.Android projesine çift tıklayın ve derleyici ayarlarına gidin.
Yapılandırma Hata Ayıklama olarak ayarlandığında Simge Tanımla'yı Android;'i içerecek şekilde düzenleyin.
Yapılandırma'yı Yayın olarak değiştirin, ardından Simge Tanımla'yı Android'i de içerecek şekilde düzenleyin;
MathFuncs.iOS için 19-20 arası adımları yineleyin ve Her iki durumda da Simgeleri Tanımla'yı Android yerine iOS'i içerecek şekilde düzenleme.
Çözümü Yayın yapılandırmasında (CONTROL + COMMAND + B) derleyin ve üç çıkış derlemesinin de (Android, iOS, .NET Standard) (ilgili proje bölmesi klasörlerinde) MathFuncs.dll aynı adı paylaştığını doğrulayın.
Bu aşamada, çözümün biri Android, iOS ve .NET Standard için olmak üzere üç hedefi ve üç hedefin her biri tarafından başvurulan paylaşılan bir proje olmalıdır. Bunlar aynı ad alanını ve aynı ada sahip çıkış derlemelerini kullanacak şekilde yapılandırılmalıdır. Bu, daha önce bahsedilen 'yem ve anahtar' yaklaşımı için gereklidir.
Yerel kitaplıkları ekleme
Sarmalayıcı çözümüne yerel kitaplık ekleme işlemi Android ve iOS arasında biraz değişiklik gösterir.
MathFuncs.Android için yerel başvurular
CONTROL + MATHFuncs.Android projesine TıKLAYıN, ardından Ekle menüsünden Yeni Klasör'e tıklayın.
Her ABI için (Uygulama İkili Arabirimi), CONTROL + Lib klasörüne TıKLAYıN, ardından Ekle menüsünden Yeni Klasör'e tıklayın ve ilgili ABI'den sonra adlandırın. Bu durumda:
- arm64-v8a
- armeabi-v7a
- x86
- x86_64
Not
Daha ayrıntılı bir genel bakış için NDK geliştirici kılavuzundaki Mimariler ve CPU'lar konusuna, özellikle uygulama paketlerinde yerel kodu ele alma bölümüne bakın.
Klasör yapısını doğrulayın:
- lib - arm64-v8a - armeabi-v7a - x86 - x86_64
Aşağıdaki eşlemeye göre abi klasörlerinin her birine karşılık gelen .so kitaplıklarını ekleyin:
arm64-v8a: lib/Android/arm64
armeabi-v7a: lib/Android/arm
x86: lib/Android/x86
x86_64: lib/Android/x86_64
Not
Dosya eklemek için CONTROL + İlgili ABI'yi temsil eden klasöre TıKLAYıN, ardından Ekle menüsünden Dosya Ekle...öğesini seçin. Uygun kitaplığı seçin (PrecompiledLibs dizininden) Aç'a tıklayın ve ardından Tamam'a tıklayarak dosyayı dizine kopyala seçeneğini varsayılan olarak bırakın.
.so dosyalarının her biri için CONTROL + CLICK tuşlarına basın ve ardından Derleme Eylemi menüsünden EmbeddedNativeLibrary seçeneğini belirleyin.
Şimdi lib klasörü aşağıdaki gibi görünmelidir:
- lib
- arm64-v8a
- libMathFuncs.so
- armeabi-v7a
- libMathFuncs.so
- x86
- libMathFuncs.so
- x86_64
- libMathFuncs.so
MathFuncs.iOS için yerel başvurular
CONTROL + MathFuncs.iOS projesine tıklayın, ardından Ekle menüsünden Yerel Başvuru Ekle'yiseçin.
libMathFuncs.a kitaplığını seçin (PrecompiledLibs dizini altındaki libs/ios'tan) ve ardından Aç'a tıklayın
CONTROL + libMathFuncs dosyasına tıklayın (Yerel Başvurular klasörünün içinde, ardından menüden Özellikler seçeneğini belirleyin
Yerel Başvuru özelliklerini, Özellikler Bölmesi'nde denetlenmeleri (bir onay işareti simgesi gösteriliyor) için yapılandırın:
- Yüklemeye Zorla
- C++ mı?
- Akıllı Bağlantı
ApiDefinition.cs açın, şablonlu açıklamalı kodu silin (yalnızca ad alanını bırakın) ve ardından Structs.cs için aynı adımı gerçekleştirin
MathFuncs
Not
Bağlama kitaplığı projesi derlemek için bu dosyaları (ObjCBindingApiDefinition ve ObjCBindingCoreSource derleme eylemleriyle) gerektirir. Ancak, standart P/Invoke kullanılarak hem Android hem de iOS kitaplık hedefleri arasında paylaşılabilen bir şekilde bu dosyaların dışında yerel kitaplığımızı çağırmak için kodu yazacağız.
Yönetilen kitaplık kodunu yazma
Şimdi yerel kitaplığı çağırmak için C# kodunu yazın. Amaç, temel alınan karmaşıklıkları gizlemektir. Tüketicinin yerel kitaplık içleri veya P/Invoke kavramları hakkında herhangi bir çalışma bilgisine ihtiyacı olmamalıdır.
Kasa Handle oluşturma
CONTROL + MATHFuncs.Shared projesine TıKLAYıN, ardından Ekle menüsünden Dosya Ekle... öğesini seçin.
Yeni Dosya penceresinde Boş Sınıf'ıseçin, myMathFuncs Kasa Handle olarak adlandırın ve Yeni'ye tıklayın
MyMathFuncs Kasa Handle sınıfını uygulayın:
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; } } }
Not
Kasa Handle, yönetilen koddaki yönetilmeyen kaynaklarla çalışmanın tercih edilen yoludur. Bu, kritik sonlandırma ve nesne yaşam döngüsüyle ilgili birçok ortak kodu soyutlar. Bu tanıtıcının sahibi daha sonra bunu başka bir yönetilen kaynak gibi değerlendirebilir ve tam Tek Kullanımlık düzeni uygulaması gerekmez.
İç sarmalayıcı sınıfı oluşturma
MyMathFuncsWrapper.cs açın ve bunu bir iç statik sınıfa değiştirme
namespace MathFuncs { internal static class MyMathFuncsWrapper { } }
Aynı dosyada aşağıdaki koşullu deyimi sınıfına ekleyin:
#if Android const string DllName = "libMathFuncs.so"; #else const string DllName = "__Internal"; #endif
Not
Bu, Kitaplığın Android veya iOS için derlenip derlenmediğine bağlı olarak DllName sabit değerini ayarlar. Bu, ilgili platformlar tarafından kullanılan farklı adlandırma kurallarının yanı sıra bu durumda kullanılan kitaplık türünü de ele almaktır. Android dinamik bir kitaplık kullanıyor ve bu nedenle uzantı da dahil olmak üzere bir dosya adı bekliyor. Statik kitaplık kullandığımız için iOS için '__Internal' gereklidir.
MyMathFuncsWrapper.cs dosyasının en üstüne System.Runtime.InteropServices başvurusu ekleme
using System.Runtime.InteropServices;
MyMathFuncs sınıfının oluşturulmasını ve atılmasını işlemek için sarmalayıcı yöntemlerini ekleyin:
[DllImport(DllName, EntryPoint = "CreateMyMathFuncsClass")] internal static extern MyMathFuncsSafeHandle CreateMyMathFuncs(); [DllImport(DllName, EntryPoint = "DisposeMyMathFuncsClass")] internal static extern void DisposeMyMathFuncs(MyMathFuncsSafeHandle ptr);
Not
DllName sabitimizi, .NET çalışma zamanına bu kitaplık içinde çağrılacak işlevin adını açıkça bildiren EntryPoint ile birlikte DllImport özniteliğine geçiriyoruz. Teknik olarak yönetilen yöntem adlarımız yönetilmeyen adla aynıysa EntryPoint değerini sağlamamız gerekmez. Sağlanmadıysa, yönetilen yöntem adı bunun yerine EntryPoint olarak kullanılır. Ancak, açık olmak daha iyidir.
Aşağıdaki kodu kullanarak MyMathFuncs sınıfıyla çalışmamızı sağlamak için sarmalayıcı yöntemlerini ekleyin:
[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);
Not
Bu örnekteki parametreler için basit türler kullanıyoruz. Bu durumda, marshalling bit düzeyinde bir kopya olduğundan, bizim tarafımızda ek bir çalışma gerektirmez. Ayrıca standart IntPtr yerine MyMathFuncs Kasa Handle sınıfının kullanıldığına da dikkat edin. IntPtr, Kasa Handle ile otomatik olarak marshalling işleminin bir parçası olarak eşlenir.
Tamamlanmış MyMathFuncsWrapper sınıfının aşağıdaki gibi göründüğünü doğrulayın:
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); } }
MyMathFuncs Kasa Handle sınıfı tamamlanıyor
MyMathFuncs Kasa Handle sınıfını açın, ReleaseHandle yöntemindeki yer tutucu TODO açıklamasına gidin:
// TODO: Release the handle here
TODO satırını değiştirin:
MyMathFuncsWrapper.DisposeMyMathFuncs(this);
MyMathFuncs sınıfını yazma
Sarmalayıcı tamamlandıktan sonra yönetilmeyen C++ MyMathFuncs nesnesine başvuruyu yönetecek bir MyMathFuncs sınıfı oluşturun.
CONTROL + MATHFuncs.Shared projesine TıKLAYıN, ardından Ekle menüsünden Dosya Ekle... öğesini seçin.
Yeni Dosya penceresinde Boş Sınıf'ıseçin, myMathFuncs olarak adlandırın ve ardından Yeni'ye tıklayın
MyMathFuncs sınıfına aşağıdaki üyeleri ekleyin:
readonly MyMathFuncsSafeHandle handle;
Sınıf örneği oluşturulurken yerel MyMathFuncs nesnesine bir tanıtıcı oluşturup depolaması için sınıfın oluşturucusunu uygulayın:
public MyMathFuncs() { handle = MyMathFuncsWrapper.CreateMyMathFuncs(); }
Aşağıdaki kodu kullanarak IDisposable arabirimini uygulayın:
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); } // ... }
Depoladığımız işaretçiyi temel alınan yönetilmeyen nesneye geçirerek arka planda gerçek işi gerçekleştirmek için MyMathFuncsWrapper sınıfını kullanarak MyMathFuncs yöntemlerini uygulayın. Kod aşağıdaki gibi olmalıdır:
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); }
Nuspec oluşturma
Kitaplığın NuGet aracılığıyla paketlenmesi ve dağıtılması için çözüm için bir nuspec dosyası gerekir. Bu, desteklenen her platform için elde edilen derlemelerden hangisinin dahil olacağını belirler.
CONTROL + ÇÖZÜME TıKLAYINMathFuncs, ardından Çözüm Klasörü Ekle menüsünden Çözüm Klasörü Ekle'yiseçin.
CONTROL + SolutionItems klasörüne TıKLAYıN, ardından Ekle menüsünden Yeni Dosya...öğesini seçin.
Yeni Dosya penceresinde Boş XML Dosyası'nı seçin, MathFuncs.nuspec olarak adlandırın ve Yeni'ye tıklayın.
MathFuncs.nuspec dosyasını NuGet tüketicisine görüntülenecek temel paket meta verileriyle güncelleştirin. Örneğin:
<?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>
Not
Bu bildirim için kullanılan şema hakkında daha fazla ayrıntı için nuspec başvuru belgesine bakın.
<files>
Öğesini öğesinin<package>
alt öğesi olarak ekleyin (hemen altında<metadata>
), her dosyayı ayrı<file>
bir öğeyle tanımlayın:<files> <!-- Android --> <!-- iOS --> <!-- netstandard2.0 --> </files>
Not
Bir paket bir projeye yüklendiğinde ve aynı adla belirtilen birden çok derleme bulunduğunda, NuGet belirli bir platforma en özel derlemeyi etkili bir şekilde seçer.
<file>
Android derlemeleri için öğeleri ekleyin:<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>
iOS derlemeleri için öğeleri ekleyin:<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>
netstandard2.0 derlemeleri için öğeleri ekleyin:<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" />
nuspec bildirimini doğrulayın:
<?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>
Not
Bu dosya bir Release derlemesinden derleme çıkış yollarını belirtir, bu nedenle çözümü bu yapılandırmayı kullanarak derlemeyi unutmayın.
Bu noktada çözüm 3 .NET derlemesi ve destekleyici bir nuspec bildirimi içerir.
.NET sarmalayıcısını NuGet ile dağıtma
Sonraki adım NuGet paketini paketleyip dağıtmaktır; böylece uygulama tarafından kolayca kullanılabilir ve bağımlılık olarak yönetilebilir. Sarmalama ve tüketimin tümü tek bir çözüm içinde yapılabilir, ancak kitaplığın NuGet aracılığıyla dağıtılması ayırmaya yardımcı olur ve bu kod temellerini bağımsız olarak yönetmemizi sağlar.
Yerel paketler dizini hazırlama
NuGet akışının en basit biçimi yerel bir dizindir:
- Bulucu'da uygun bir dizine gidin. Örneğin, /Users.
- Dosya menüsünden Yeni Klasör'e tıklayarak local-nuget-feed gibi anlamlı bir ad sağlayın.
Paketi oluşturma
Derleme Yapılandırması'nıYayın olarak ayarlayın ve COMMAND + B kullanarak bir derleme yürütür.
Terminal'i açın ve dizini nuspec dosyasını içeren klasörle değiştirin.
Terminal'de nuspec dosyasını, Sürüm'leri (örneğin, 1.0.0) ve OutputDirectory'yi belirten nuget pack komutunu, önceki adımda oluşturulmuş olan yerel-nuget-feed klasörünü kullanarak yürütebilirsiniz. Örneğin:
nuget pack MathFuncs.nuspec -Version 1.0.0 -OutputDirectory ~/local-nuget-feed
MathFuncs.1.0.0.nupkg dosyasının yerel-nuget-feed dizininde oluşturulduğunu onaylayın.
[İSTEĞE BAĞLı] Azure DevOps ile özel NuGet akışı kullanma
Azure DevOps'ta NuGet paketlerini kullanmaya başlama bölümünde özel akış oluşturma ve paketin (önceki adımda oluşturulan) bu akışa nasıl gönderildiğini gösteren daha sağlam bir teknik açıklanmıştır.
Bu iş akışının, örneğin Azure Pipelines'ı kullanarak tamamen otomatik hale getirmek idealdir. Daha fazla bilgi için bkz . Azure Pipelines'ı kullanmaya başlama.
Xamarin.Forms uygulamasından .NET sarmalayıcısını kullanma
İzlenecek yolu tamamlamak için, yerel NuGet akışında yayımlanan paketi kullanacak bir Xamarin.Forms uygulaması oluşturun.
Xamarin.Forms projesi oluşturma
yeni bir Mac için Visual Studio örneği açın. Bu işlem Terminal'den yapılabilir:
open -n -a "Visual Studio"
Mac için Visual Studio'da, Yeni Proje'ye (Hoş Geldiniz Sayfasından) veya Yeni Çözüm'e (Dosya menüsünden) tıklayın.
Yeni Proje penceresinde Boş Formlar Uygulaması'nı seçin (Çok Platformlu > Uygulama'nın içinden) ve ardından İleri'ye tıklayın.
Aşağıdaki alanları güncelleştirin ve İleri'ye tıklayın:
- Uygulama Adı: MathFuncsApp.
- Kuruluş Tanımlayıcısı: Ters ad alanı kullanın; örneğin, com.{your_org}.
- Hedef Platformlar: Varsayılanı kullanın (hem Android hem de iOS hedefleri).
- Paylaşılan Kod: Bunu .NET Standard olarak ayarlayın (bir "Paylaşılan Kitaplık" çözümü mümkündür, ancak bu kılavuzun kapsamı dışındadır).
Aşağıdaki alanları güncelleştirin ve Oluştur'a tıklayın:
- Proje Adı: MathFuncsApp.
- Çözüm Adı: MathFuncsApp.
- Konum: Varsayılan kaydetme konumunu kullanın (veya alternatif bir konum seçin).
Çözüm Gezgini ilk test için CONTROL + HEDEFE (MathFuncsApp.Android veya MathFuncs.iOS) tıklayın, ardından Başlangıç Projesi Olarak Ayarla'yı seçin.
Tercih edilen cihazı veya Simülatör Öykünücüsü'ne/ tıklayın.
Şablonlu Xamarin.Forms projesinin derlendiğini ve düzgün çalıştığını doğrulamak için çözümü (COMMAND + RETURN) çalıştırın.
Not
iOS (özellikle simülatör) en hızlı derleme/dağıtım süresine sahip olma eğilimindedir.
Yerel NuGet akışını NuGet yapılandırmasına ekleme
Visual Studio'da Tercihler'i seçin (Visual Studio menüsünden).
NuGet bölümünün altındaki Kaynaklardan'ı seçin ve ekle'ye tıklayın.
Aşağıdaki alanları güncelleştirin ve Kaynak Ekle'ye tıklayın:
- Ad: Yerel Paketler gibi anlamlı bir ad sağlayın.
- Konum: Önceki adımda oluşturulan local-nuget-feed klasörünü belirtin.
Not
Bu durumda Kullanıcı Adı ve Parola belirtmeniz gerekmez.
Tamam'a tıklayın.
Pakete başvurma
Her proje için aşağıdaki adımları yineleyin (MathFuncsApp, MathFuncsApp.Android ve MathFuncsApp.iOS).
- CONTROL + Projeye TıKLAYıN, ardından Ekle menüsünden NuGet Paketleri Ekle... öğesini seçin.
- MathFuncs araması yapın.
- Paketin Sürümünün 1.0.0 olduğunu ve Başlık ve Açıklama, yani MathFuncs ve Sample C++ Sarmalayıcı Kitaplığı gibi diğer ayrıntıların beklendiği gibi göründüğünü doğrulayın.
- MathFuncs paketini seçin ve ardından Paket Ekle'ye tıklayın.
Kitaplık işlevlerini kullanma
Şimdi, projelerin her birinde MathFuncs paketine bir başvuruyla, işlevler C# kodu tarafından kullanılabilir.
MathFuncsApp ortak Xamarin.Formsprojesinin içinden MainPage.xaml.cs açın (hem MathFuncsApp.Android hem de MathFuncsApp.iOS tarafından başvurulur).
Dosyanın en üstüne System.Diagnostics ve MathFuncs için using deyimleri ekleyin:
using System.Diagnostics; using MathFuncs;
Sınıfının en üstünde bir örneğini
MyMathFuncs
bildirinMainPage
:MyMathFuncs myMathFuncs;
OnAppearing
OnDisappearing
ve yöntemlerini temel sınıftanContentPage
geçersiz kılın:protected override void OnAppearing() { base.OnAppearing(); } protected override void OnDisappearing() { base.OnDisappearing(); }
OnAppearing
Daha önce bildirilen değişkeni başlatmak için yöntemini güncelleştirinmyMathFuncs
:protected override void OnAppearing() { base.OnAppearing(); myMathFuncs = new MyMathFuncs(); }
OnDisappearing
yöntemini üzerinde çağıracak şekildeDispose
güncelleştirinmyMathFuncs
:protected override void OnDisappearing() { base.OnAppearing(); myMathFuncs.Dispose(); }
TestMathFuncs adlı özel bir yöntemi aşağıdaki gibi uygulayın:
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}"); }
Son olarak,
TestMathFuncs
yönteminin sonunda çağrısı:OnAppearing
TestMathFuncs();
Uygulamayı her hedef platformda çalıştırın ve Uygulama Çıkış Bölmesi'ndeki çıkışın aşağıdaki gibi göründüğünü doğrulayın:
1 + 2 = 3 1 - 2 = -1 1 * 2 = 2 1 / 2 = 0.5
Not
Android'de test ederken 'DLLNotFoundException' veya iOS'ta derleme hatasıyla karşılaşırsanız, kullandığınız cihazın/öykünücünün/simülatörün CPU mimarisinin desteklemeyi seçtiğimiz alt kümeyle uyumlu olup olmadığını kontrol ettiğinizden emin olun.
Özet
Bu makalede, NuGet paketi aracılığıyla dağıtılan ortak bir .NET sarmalayıcısı aracılığıyla yerel kitaplıkları kullanan bir Xamarin.Forms uygulamasının nasıl oluşturulacağı açıklanmıştır. Bu kılavuzda sağlanan örnek, yaklaşımı daha kolay göstermek için kasıtlı olarak çok basittir. Gerçek bir uygulamanın özel durum işleme, geri çağırmalar, daha karmaşık türlerin sıralanması ve diğer bağımlılık kitaplıklarıyla bağlantı oluşturma gibi karmaşıklıklarla ilgilenmesi gerekir. Önemli bir nokta, C++ kodunun evriminin sarmalayıcı ve istemci uygulamalarıyla eşgüdümlü ve eşitlenmesi sürecidir. Bu süreç, bu endişelerden birinin veya her ikisinin tek bir ekibin sorumluluğunda olup olmadığına bağlı olarak değişebilir. Her iki durumda da otomasyon gerçek bir avantajdır. Aşağıda, bazı önemli kavramlar ve ilgili indirmeler hakkında daha fazla bilgi sağlayan bazı kaynaklar yer almaktadır.