Xamarin.Android API Tasarım İlkeleri

Mono'nun parçası olan temel Temel Sınıf Kitaplıklarına ek olarak Xamarin.Android, geliştiricilerin Mono ile yerel Android uygulamaları oluşturmasına olanak sağlamak için çeşitli Android API'lerine yönelik bağlamalarla birlikte gelir.

Xamarin.Android'in merkezinde C# dünyasını Java dünyası ile bir araya getiren ve geliştiricilere C# veya diğer .NET dillerinden Java API'lerine erişim sağlayan bir birlikte çalışma altyapısı vardır.

Tasarım İlkeleri

Bunlar, Xamarin.Android bağlaması için tasarım ilkelerimizden bazılarıdır

  • .NET Framework Tasarım Yönergeleri'ne uygun olun.

  • Geliştiricilerin Java sınıflarını alt sınıfına eklemesine izin verin.

  • Alt sınıf C# standart yapılarıyla çalışmalıdır.

  • Var olan bir sınıftan türetilir.

  • Zincire temel oluşturucuyu çağırın.

  • Geçersiz kılma yöntemleri C# 'nin geçersiz kılma sistemi ile yapılmalıdır.

  • Yaygın Java görevlerini kolaylaştırma ve sabit Java görevlerini mümkün hale getirme.

  • JavaBean özelliklerini C# özellikleri olarak kullanıma sunma.

  • Kesin türü belirlenmiş bir API'nin kullanıma sunma:

    • Tür güvenliğini artırın.

    • Çalışma zamanı hatalarını en aza indirin.

    • dönüş türlerinde IDE intellisense'i edinin.

    • IDE açılır belgelerine izin verir.

  • API'lerin IDE içinde keşfini teşvik edin:

    • Java Classlib maruziyetini en aza indirmek için Framework Alternatiflerini kullanma.

    • Uygun ve uygun olduğunda tek yöntemli arabirimler yerine C# temsilcilerini (lambdas, anonim yöntemler ve System.Delegate) kullanıma sunma.

    • Rastgele Java kitaplıklarını çağırmak için bir mekanizma sağlayın ( Android.Runtime.JNIEnv).

Bütünleştirilmiş Kodlar

Xamarin.Android, MonoMobile Profilini oluşturan bir dizi derleme içerir. Derlemeler sayfasında daha fazla bilgi bulunur.

Android platformuna yönelik bağlamalar derlemede Mono.Android.dll yer alır. Bu derleme, Android API'lerini kullanmaya ve Android çalışma zamanı VM'siyle iletişim kurmaya yönelik bağlamanın tamamını içerir.

Bağlama Tasarımı

Koleksiyonlar

Android API'leri listeler, kümeler ve haritalar sağlamak için java.util koleksiyonlarını kapsamlı bir şekilde kullanır. Bağlamamızda System.Collections.Generic arabirimlerini kullanarak bu öğeleri kullanıma sunarız. Temel eşlemeler şunlardır:

Bu türlerin daha hızlı kopyasız şekilde sıralanmasını kolaylaştırmak için yardımcı sınıflar sağladık. Mümkün olduğunda, veya Dictionary<TKey, TValue>gibi List<T> sağlanan çerçeve uygulaması yerine sağlanan bu koleksiyonları kullanmanızı öneririz. Android.Runtime uygulamaları yerel bir Java koleksiyonunu dahili olarak kullanır ve bu nedenle bir Android API üyesine geçerken yerel bir koleksiyona ve yerel koleksiyondan kopyalama gerektirmez.

Herhangi bir arabirim uygulamasını bu arabirimi kabul eden bir Android yöntemine geçirebilirsiniz; örneğin ArrayAdapter<int(Context, int>, IList<int>) oluşturucusunda bir List<int>geçirin. Ancak, Android.Runtime uygulamaları dışındaki tüm uygulamalar için bu, listeyi Mono VM'den Android çalışma zamanı VM'sine kopyalamayı içerir. Liste daha sonra Android çalışma zamanı içinde değiştirilirse (örneğin ArrayAdapter<T'yi >çağırarak). Add(T) yöntemi), bu değişiklikler yönetilen kodda görünmez. JavaList<int> kullanılsaydı, bu değişiklikler görünür olurdu.

Yeniden ifade edildi, yukarıda listelenen Yardımcı Sınıfes'lerden biri olmayan koleksiyon arabirimi uygulamaları yalnızca sıralama [In]:

// This fails:
var badSource  = new List<int> { 1, 2, 3 };
var badAdapter = new ArrayAdapter<int>(context, textViewResourceId, badSource);
badAdapter.Add (4);
if (badSource.Count != 4) // true
    throw new InvalidOperationException ("this is thrown");

// this works:
var goodSource  = new JavaList<int> { 1, 2, 3 };
var goodAdapter = new ArrayAdapter<int> (context, textViewResourceId, goodSource);
goodAdapter.Add (4);
if (goodSource.Count != 4) // false
    throw new InvalidOperationException ("should not be reached.");

Properties

Java yöntemleri, uygun olduğunda özelliklere dönüştürülür:

  • Java yöntemi çifti T getFoo() ve void setFoo(T) özelliğine Foo dönüştürülür. Örnek: Activity.Intent.

  • Java yöntemi getFoo() salt okunur Foo özelliğine dönüştürülür. Örnek: Context.PackageName.

  • Yalnızca ayarlama özellikleri oluşturulmaz.

  • Özellik türü bir dizi olacaksa özellikler oluşturulmaz.

Olaylar ve Dinleyiciler

Android API'leri Java üzerine kuruludur ve bileşenleri, olay dinleyicilerini bağlamak için Java desenini izler. Kullanıcının anonim bir sınıf oluşturmasını ve geçersiz kılınacak yöntemleri bildirmesini gerektirdiğinden, örneğin, Java ile Android'de işler şu şekilde yapılırdı:

final android.widget.Button button = new android.widget.Button(context);

button.setText(this.count + " clicks!");
button.setOnClickListener (new View.OnClickListener() {
    public void onClick (View v) {
        button.setText(++this.count + " clicks!");
    }
});

C# dilinde olayları kullanan eşdeğer kod şöyle olabilir:

var button = new Android.Widget.Button (context) {
    Text = string.Format ("{0} clicks!", this.count),
};
button.Click += (sender, e) => {
    button.Text = string.Format ("{0} clicks!", ++this.count);
};

Yukarıdaki mekanizmaların her ikisinin de Xamarin.Android ile kullanılabilir olduğunu unutmayın. Bir dinleyici arabirimi uygulayıp View.SetOnClickListener ile ekleyebilir veya Her zamanki C# paradigmalarından herhangi biri aracılığıyla oluşturulan bir temsilciyi Click olayına ekleyebilirsiniz.

Dinleyici geri çağırma yönteminin geçersiz bir dönüşü olduğunda, EventHandler <TEventArgs> temsilcisini temel alan API öğeleri oluştururuz. Bu dinleyici türleri için yukarıdaki örneğe benzer bir olay oluştururuz. Ancak, dinleyici geri çağrısı geçersiz olmayan ve boole olmayan bir değer döndürürse, olaylar ve EventHandlers kullanılmaz. Bunun yerine, geri çağırmanın imzası için belirli bir temsilci oluşturur ve olaylar yerine özellikler ekleriz. Bunun nedeni, temsilci çağırma sırası ve iade işleme ile ilgilenmektir. Bu yaklaşım, Xamarin.iOS API'siyle yapılan işlemleri yansıtır.

C# olayları veya özellikleri yalnızca Android olay kayıt yöntemi:

  1. set Ön eke sahiptir; örneğin OnClickListener'ı ayarlayın.

  2. Dönüş türüne void sahiptir.

  3. Yalnızca bir parametreyi kabul eder, parametre türü bir arabirimdir, arabirimin tek bir yöntemi vardır ve arabirim adı ile biter Listener ; örneğin View.OnClick Dinleyicisi.

Ayrıca, Dinleyici arabirimi yöntemi void yerine boole dönüş türüne sahipse, oluşturulan EventArgs alt sınıfı bir Handledözelliği içerir. Handled özelliğinin değeri, Dinleyici yöntemi için dönüş değeri olarak kullanılır ve varsayılan olarak olarak truekullanılır.

Örneğin, Android View.setOnKeyListener() yöntemi View.OnKeyListener arabirimini kabul eder ve View.OnKeyListener.onKey(View, int, KeyEvent) yönteminin boole dönüş türü vardır. Xamarin.Android, eventHandler View.KeyEventArgs> olan karşılık gelen bir View.KeyPress olayı<oluşturur. KeyEventArgs sınıfı da View.OnKeyListener.onKey() yönteminin dönüş değeri olarak kullanılan View.KeyEventArgs.Handled özelliğine sahiptir.

Temsilci tabanlı bağlantıyı kullanıma sunma amacıyla diğer yöntemler ve ctor'lar için aşırı yüklemeler eklemeyi planlıyoruz. Ayrıca, birden çok geri çağırması olan dinleyiciler, tek tek geri çağırmaların uygulanmasının makul olup olmadığını belirlemek için bazı ek incelemeler gerektirir, bu nedenle bunları tanımlandıkları şekilde dönüştürüyoruz. Buna karşılık gelen bir olay yoksa, dinleyicilerin C# dilinde kullanılması gerekir, ancak lütfen temsilci kullanımına sahip olabileceğini düşündüğünüz herhangi birini dikkatimize getirin. Ayrıca, temsilci alternatifinden yararlanabilecekleri açık olduğunda "Dinleyici" soneki olmadan bazı arabirim dönüştürmeleri de yaptık.

Tüm dinleyici arabirimleri Android.Runtime.IJavaObject arabirimi, bağlamanın uygulama ayrıntıları nedeniyle dinleyici sınıflarının bu arabirimi uygulaması gerekir. Bu, Java.Lang.Object alt sınıfına veya Android etkinliği gibi başka bir sarmalanmış Java nesnesine dinleyici arabirimi uygulanarak yapılabilir.

Çalıştırılabilir Öğeler

Java, temsilci mekanizması sağlamak için java.lang.Runnable arabirimini kullanır. java.lang.Thread sınıfı, bu arabirimin önemli bir tüketicisidir. Android de API'de arabirimini kullanmıştır. Activity.runOnUiThread() ve View.post() önemli örneklerdir.

Arabirim Runnable , run() adlı tek bir void yöntemi içerir. Bu nedenle, kendisini C# dilinde System.Action temsilcisi olarak bağlamaya ödünç verir. Bağlamada, yerel API'de ( örneğin Activity.RunOnUiThread() ve View.Post() kullanan Runnable tüm API üyeleri için bir Action parametre kabul eden aşırı yüklemeler sağladık.

Çeşitli türler arabirimi uyguladığından ve bu nedenle doğrudan çalıştırılabilir olarak geçirilebildiğinden, IRunnable aşırı yüklemelerini değiştirmek yerine yerinde bıraktık.

İç Sınıflar

Java'nın iki farklı iç içe sınıf türü vardır: statik iç içe sınıflar ve statik olmayan sınıflar.

Java statik iç içe sınıfları C# iç içe türleriyle aynıdır.

İç sınıflar olarak da adlandırılan statik olmayan iç içe sınıflar önemli ölçüde farklıdır. Bunlar, kapsayan türlerinin bir örneğine örtük bir başvuru içerir ve statik üyeler içeremez (bu genel bakışın kapsamı dışındaki diğer farklılıklar arasında).

Bağlama ve C# kullanımı söz konusu olduğunda, statik iç içe sınıflar normal iç içe türler olarak kabul edilir. Bu arada iç sınıfların iki önemli farkı vardır:

  1. İçeren türe örtük başvuru, oluşturucu parametresi olarak açıkça sağlanmalıdır.

  2. İç sınıftan devralınırken, iç sınıf, temel iç sınıfın içeren türünden devralan bir tür içinde iç içe yerleştirilmelidir ve türetilen tür, türü içeren C# ile aynı türde bir oluşturucu sağlamalıdır.

Örneğin, Android.Service.Wallpaper.WallpaperService.Engine iç sınıfını göz önünde bulundurun. İç sınıf olduğundan WallpaperService.Engine() oluşturucu bir WallpaperService örneğine başvuru alır (parametre içermeyen Java WallpaperService.Engine() oluşturucusunun karşılaştırması ve karşıtlığı).

Bir iç sınıfın örnek türetme işlemi CubeWallpaper.CubeEngine'dir:

class CubeWallpaper : WallpaperService {
    public override WallpaperService.Engine OnCreateEngine ()
    {
        return new CubeEngine (this);
    }

    class CubeEngine : WallpaperService.Engine {
        public CubeEngine (CubeWallpaper s)
                : base (s)
        {
        }
    }
}

içinde CubeWallpaperCubeWallpaper nasıl CubeWallpaper.CubeEngine iç içe yerleştirildiğine dikkat edin, öğesini içeren sınıfından WallpaperService.Enginedevralır ve CubeWallpaper.CubeEngine bildirim türünü ( CubeWallpaper bu örnekte) alan bir oluşturucuya sahiptir.

Arabirimler

Java arabirimleri üç üye kümesi içerebilir ve bu kümelerden ikisi C# ile ilgili sorunlara neden olur:

  1. Yöntemler

  2. Türler

  3. Alanlar

Java arabirimleri iki türe çevrilir:

  1. Yöntem bildirimlerini içeren bir (isteğe bağlı) arabirim. Bu arabirim, Java arabirimiyle aynı ada sahiptir, ancak ' I ' ön ekini de içerir.

  2. Java arabiriminde bildirilen alanları içeren bir (isteğe bağlı) statik sınıf.

İç içe türler, iç içe türler yerine kapsayan arabirimin eşdüzeyleri olacak şekilde "yeniden konumlandırılır", bunu kapsayan arabirim adı ön ek olarak alır.

Örneğin android.os.Parcelable arabirimini göz önünde bulundurun. Parselleştirilebilir arabirimi yöntemler, iç içe türler ve sabitler içerir. Parselleştirilebilir arabirim yöntemleri Android.OS.IParcelable arabirimine yerleştirilir. Parselleştirilebilir arabirim sabitleri Android.OS.ParcelableConsts türüne yerleştirilir. İç içe geçmiş android.os.Parcelable.ClassLoaderCreator<T> ve android.os.Parcelable.Creator<T> türleri şu anda genel öğe desteğimizdeki sınırlamalar nedeniyle bağlı değildir; destekleniyorlarsa Android.OS.IParcelableClassLoaderCreator ve Android.OS.IParcelableCreator arabirimleri olarak mevcut olur. Örneğin, iç içe yerleştirilmiş android.os.IBinder.DeathRecipient arabirimi Android.OS.IBinderDeathRecipient arabirimi olarak bağlıdır.

Not

Xamarin.Android 1.9 sürümünden başlayarak Java arabirimi sabitleri Java kodunu taşımayı basitleştirmek amacıyla çoğaltılır. Bu, Android sağlayıcı arabirimi sabitlerini kullanan Java kodunu taşımayı geliştirmeye yardımcı olur.

Yukarıdaki türlere ek olarak dört değişiklik daha vardır:

  1. Sabitleri içerecek şekilde Java arabirimiyle aynı ada sahip bir tür oluşturulur.

  2. Arabirim sabitlerini içeren türler, uygulanan Java arabirimlerinden gelen tüm sabitleri de içerir.

  3. Sabitleri içeren bir Java arabirimi uygulayan tüm sınıflar, uygulanan tüm arabirimlerden sabitleri içeren yeni bir iç içe InterfaceConsts türü alır.

  4. Consts türü artık kullanımdan kaldırıldı.

android.os.Parcelable arabirimi için bu, artık sabitleri içeren bir Android.OS.Parcelable türü olacağı anlamına gelir. Örneğin, Parcelable.CONTENTS_FILE_DESCRIPTOR sabiti, ParcelableConsts.ContentsFileDescriptor sabiti yerine Parcelable.ContentsFileDescriptor sabiti olarak bağlanır.

Daha fazla sabit içeren diğer arabirimleri uygulayan sabitleri içeren arabirimler için artık tüm sabitlerin birleşimi oluşturulur. Örneğin, android.provider.MediaStore.Video.VideoColumns arabirimi android.provider.MediaStore.MediaColumns arabirimini uygular. Ancak, 1.9'dan önce Android.Provider.MediaStore.Video.VideoColumnsConsts türünün Android.Provider.MediaStore.MediaColumnsConsts üzerinde bildirilen sabitlere erişme yolu yoktur. Sonuç olarak, MediaStore.Video.VideoColumns.TITLE Java ifadesinin, çok sayıda Java belgesini okumadan bulunması zor olan MediaStore.Video.MediaColumnsConsts.Title C# ifadesine bağlı olması gerekir. 1.9'da eşdeğer C# ifadesi MediaStore.Video.VideoColumns.Title olacaktır.

Ayrıca Java Paketlenebilir arabirimini uygulayan android.os.Bundle türünü de göz önünde bulundurun. Arabirimi uyguladığından, bu arabirimdeki tüm sabitlere Paket türü üzerinden "" erişilebilir; örneğin , Bundle.CONTENTS_FILE_DESCRIPTOR mükemmel geçerli bir Java ifadesidir. Daha önce, bu ifadeyi C# olarak taşımanız için, CONTENTS_FILE_DESCRIPTOR hangi türden geldiğini görmek için uygulanan tüm arabirimlere bakmanız gerekirdi. Xamarin.Android 1.9'dan başlayarak, sabitler içeren Java arabirimleri uygulayan sınıflar, devralınan tüm arabirim sabitlerini içerecek iç içe bir InterfaceConsts türüne sahip olacaktır. Bu, Bundle.CONTENTS_FILE_DESCRIPTOR Bundle.InterfaceConsts.ContentsFileDescriptor'a çevirmeye olanak sağlar.

Son olarak, Android.OS.ParcelableConsts gibi bir Consts soneki olan türler, yeni kullanıma sunulan InterfaceConsts iç içe türleri dışında artık kullanım dışıdır. Bunlar Xamarin.Android 3.0'da kaldırılacaktır.

Kaynaklar

Resimler, düzen açıklamaları, ikili bloblar ve dize sözlükleri uygulamanıza kaynak dosyaları olarak eklenebilir. Çeşitli Android API'leri görüntüler, dizeler veya ikili bloblarla doğrudan ilgilenmek yerine kaynak kimlikleri üzerinde çalışacak şekilde tasarlanmıştır.

Örneğin, kullanıcı arabirimi düzeni ( main.axml), bir uluslararasılaştırma tablosu dizesi ( strings.xml) ve bazı simgeler ( drawable-*/icon.png) içeren örnek bir Android uygulaması, kaynaklarını uygulamanın "Resources" dizininde tutar:

Resources/
    drawable-hdpi/
        icon.png

    drawable-ldpi/
        icon.png

    drawable-mdpi/
        icon.png

    layout/
        main.axml

    values/
        strings.xml

Yerel Android API'leri doğrudan dosya adlarıyla çalışmaz, bunun yerine kaynak kimlikleri üzerinde çalışır. Kaynakları kullanan bir Android uygulamasını derlediğinizde, derleme sistemi kaynakları dağıtım için paketler ve dahil edilen her bir kaynak için belirteçleri içeren adlı Resource bir sınıf oluşturur. Örneğin, yukarıdaki Kaynaklar düzeni için R sınıfının ortaya çıkaracağı şey budur:

public class Resource {
    public class Drawable {
        public const int icon = 0x123;
    }

    public class Layout {
        public const int main = 0x456;
    }

    public class String {
        public const int first_string = 0xabc;
        public const int second_string = 0xbcd;
    }
}

Daha sonra dosyasına başvurmak drawable/icon.png veya dosyaya başvurmak layout/main.xml ya Resource.Layout.main da Resource.String.first_string sözlük dosyasındaki values/strings.xmlilk dizeye başvurmak için kullanırsınızResource.Drawable.icon.

Sabitler ve Numaralandırmalar

Yerel Android API'leri, int'in ne anlama geldiğini belirlemek için sabit bir alana eşlenmesi gereken bir int alan veya döndüren birçok yönteme sahiptir. Bu yöntemleri kullanmak için, kullanıcının hangi sabitlerin uygun değerler olduğunu ve ideal değerlerden daha az olduğunu görmek için belgelere başvurması gerekir.

Örneğin, Activity.requestWindowFeature(int featureID) konusunu göz önünde bulundurun.

Bu gibi durumlarda, ilgili sabitleri bir .NET sabit listesi halinde gruplandırmaya çalışıyoruz ve bunun yerine numaralandırmayı almak için yöntemini yeniden eşledik. Bunu yaparak olası değerler için IntelliSense seçimi sunabiliyoruz.

Yukarıdaki örnek şöyle olur: Activity.RequestWindowFeature(WindowFeatures featureId).

Bunun, hangi sabitlerin birbirine ait olduğunu ve hangi API'lerin bu sabitleri tükettiğine dair el ile gerçekleştirilen bir işlem olduğunu unutmayın. Lütfen API'de kullanılan ve sabit listesi olarak daha iyi ifade edilecek sabitler için hataları dosyalayın.