Aracılığıyla paylaş


JNI ve Xamarin.Android ile çalışma

Xamarin.Android, Java yerine C# ile Android uygulamaları yazmaya izin verir. Mono.Android.dll ve Mono.Android.Google Haritalar.dll gibi Java kitaplıkları için bağlamalar sağlayan Xamarin.Android ile çeşitli derlemeler sağlanır. Ancak, olası her Java kitaplığı için bağlamalar sağlanmaz ve sağlanan bağlamalar her Java türünü ve üyesini bağlamayabilir. İlişkisiz Java türlerini ve üyelerini kullanmak için Java Yerel Arabirimi (JNI) kullanılabilir. Bu makalede, Xamarin.Android uygulamalarından Java türleri ve üyeleriyle etkileşime geçmek için JNI'nin nasıl kullanılacağı gösterilmektedir.

Genel bakış

Java kodunu çağırmak için Yönetilen Çağrılabilir Sarmalayıcı (MCW) oluşturmak her zaman gerekli veya mümkün değildir. Çoğu durumda, "satır içi" JNI, ilişkisiz Java üyelerinin tek seferlik kullanımı için son derece kabul edilebilir ve kullanışlıdır. Java sınıfında tek bir yöntemi çağırmak için JNI kullanmak genellikle .jar bağlamanın tamamını oluşturmaktan daha kolaydır.

Xamarin.Android, Android android.jar kitaplığı için bir bağlama sağlayan derlemeyi sağlarMono.Android.dll. İçinde bulunmayan Mono.Android.dll türler ve üyeler ve içinde android.jar mevcut olmayan türler el ile bağlanarak kullanılabilir. Java türlerini ve üyelerini bağlamak için Java Yerel Arabirimi'ni (JNI) kullanarak türleri arar, alanları okur ve yazar ve yöntemleri çağırırsınız.

Xamarin.Android'deki JNI API'si kavramsal olarak .NET'teki API'ye çok benzer System.Reflection : türleri ve üyeleri ada göre aramanızı, alan değerlerini okumanızı ve yazmanızı, yöntemleri çağırmanızı ve daha fazlasını yapabilirsiniz. Geçersiz kılmayı desteklemeye Android.Runtime.RegisterAttribute bağlı olabilecek sanal yöntemleri bildirmek için JNI ve özel özniteliğini kullanabilirsiniz. C# dilinde uygulanabilmeleri için arabirimleri bağlayabilirsiniz.

Bu belgede şu açıklanmaktadır:

  • JNI'nin türlere nasıl başvurduğu.
  • Alanları arama, okuma ve yazma.
  • Yöntemleri arama ve çağırma.
  • Yönetilen koddan geçersiz kılmaya izin vermek için sanal yöntemleri kullanıma sunma.
  • Arabirimleri kullanıma sunma.

Gereksinimler

Android.Runtime.JNIEnv ad alanı aracılığıyla kullanıma sunulan JNI, Xamarin.Android'in her sürümünde kullanılabilir. Java türlerini ve arabirimlerini bağlamak için Xamarin.Android 4.0 veya üzerini kullanmanız gerekir.

Yönetilen Çağrılabilen Sarmalayıcılar

Yönetilen Çağrılabilen Sarmalayıcı (MCW), istemci C# kodunun JNI'nin temel karmaşıklığı konusunda endişelenmesi gerekmeyecek şekilde tüm JNI makinelerini sarmalayan bir Java sınıfı veya arabirimine yönelik bir bağlamadır. Çoğu Mono.Android.dll , yönetilen çağrılabilen sarmalayıcılardan oluşur.

Yönetilen çağrılabilen sarmalayıcılar iki amaca hizmet eder:

  1. İstemci kodunun temel karmaşıklık hakkında bilgi sahibi olması gerekmeyecek şekilde JNI kullanımını kapsülleme.
  2. Java türlerini alt sınıfa alıp Java arabirimleri uygulamayı mümkün hale getirin.

İlk amaç yalnızca kolaylık sağlamak ve karmaşıklığı kapsüllemektir, böylece tüketicilerin kullanabilecekleri basit, yönetilen bir sınıf kümesi vardır. Bu, bu makalenin devamında açıklandığı gibi çeşitli JNIEnv üyelerinin kullanılmasını gerektirir. Yönetilen çağrılabilen sarmalayıcıların kesinlikle gerekli olmadığını unutmayın; "satır içi" JNI kullanımı son derece kabul edilebilir ve ilişkisiz Java üyelerinin tek seferlik kullanımı için yararlıdır. Alt sınıflama ve arabirim uygulaması, yönetilen çağrılabilen sarmalayıcıların kullanılmasını gerektirir.

Android Çağrılabilir Sarmalayıcıları

Android çalışma zamanının (ART) yönetilen kodu çağırması gerektiğinde Android çağrılabilir sarmalayıcılar (ACW) gerekir; bu sarmalayıcılar gereklidir çünkü çalışma zamanında SıNıFLARı ART'a kaydetmenin bir yolu yoktur. (Özellikle, DefineClass JNI işlevi Android çalışma zamanı tarafından desteklenmez. Android çağrılabilen sarmalayıcılar bu nedenle çalışma zamanı türü kayıt desteği eksikliğini telafi etti.)

Android kodunun geçersiz kılınan veya yönetilen kodda uygulanan bir sanal veya arabirim yöntemini yürütmesi gerektiğinde, Xamarin.Android'in bu yöntemin uygun yönetilen türe gönderilmesi için bir Java ara sunucusu sağlaması gerekir. Bu Java proxy türleri, yönetilen türle "aynı" temel sınıfa ve Java arabirim listesine sahip, aynı oluşturucuları uygulayan ve geçersiz kılınan temel sınıf ve arabirim yöntemlerini belirten Java kodulardır.

Android çağrılabilen sarmalayıcılar, derleme işlemi sırasında monodroid.exe programı tarafından oluşturulur ve Java.Lang.Object'i devralan (doğrudan veya dolaylı) tüm türler için oluşturulur.

Arabirimleri Uygulama

Bir Android arabirimi (Android.Content.IComponentCallbacks gibi) uygulamanız gerekebilecek zamanlar olabilir.

Tüm Android sınıfları ve arabirimleri Android.Runtime.IJavaObject arabirimini genişletir; bu nedenle, tüm Android türlerinin uygulaması IJavaObjectgerekir. Xamarin.Android bu olgudan yararlanır– Android'e belirli bir yönetilen tür için bir Java ara sunucusu (Android çağrılabilen sarmalayıcı) sağlamak için kullanır IJavaObject . monodroid.exe yalnızca alt sınıfları (uygulaması IJavaObjectgerekir) aradığındanJava.Lang.Object, alt sınıflama Java.Lang.Object yönetilen kodda arabirimleri uygulamamız için bir yol sağlar. Örneğin:

class MyComponentCallbacks : Java.Lang.Object, Android.Content.IComponentCallbacks {
    public void OnConfigurationChanged (Android.Content.Res.Configuration newConfig) {
        // implementation goes here...
    }
    public void OnLowMemory () {
        // implementation goes here...
    }
}

Uygulama Ayrıntıları

Bu makalenin geri kalanında bildirimde bulunulmadan değiştirilebilir uygulama ayrıntıları sağlanır (ve burada sunulur çünkü geliştiriciler arka planda neler olup bittiğini merak edebilir).

Örneğin, aşağıdaki C# kaynağı göz önünde bulundurulduğunda:

using System;
using Android.App;
using Android.OS;

namespace Mono.Samples.HelloWorld
{
    public class HelloAndroid : Activity
    {
        protected override void OnCreate (Bundle savedInstanceState)
        {
            base.OnCreate (savedInstanceState);
            SetContentView (R.layout.main);
        }
    }
}

mandroid.exe programı aşağıdaki Android Çağrılabilen Sarmalayıcıyı oluşturur:

package mono.samples.helloWorld;

public class HelloAndroid extends android.app.Activity {
    static final String __md_methods;
    static {
        __md_methods =
            "n_onCreate:(Landroid/os/Bundle;)V:GetOnCreate_Landroid_os_Bundle_Handler\n" +
            "";
        mono.android.Runtime.register (
                "Mono.Samples.HelloWorld.HelloAndroid, HelloWorld, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
                HelloAndroid.class,
                __md_methods);
    }

    public HelloAndroid ()
    {
        super ();
        if (getClass () == HelloAndroid.class)
            mono.android.TypeManager.Activate (
                "Mono.Samples.HelloWorld.HelloAndroid, HelloWorld, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
                "", this, new java.lang.Object[] { });
    }

    @Override
    public void onCreate (android.os.Bundle p0)
    {
        n_onCreate (p0);
    }

    private native void n_onCreate (android.os.Bundle p0);
}

Temel sınıfın korunduğuna ve yönetilen kod içinde geçersiz kılınan her yöntem için yerel yöntem bildirimleri sağlandığına dikkat edin.

ExportAttribute ve ExportFieldAttribute

Genellikle Xamarin.Android, ACW'yi oluşturan Java kodunu otomatik olarak oluşturur; bu nesil, bir sınıf java sınıfından türetildiğinde ve mevcut Java yöntemlerini geçersiz kıldığında sınıf ve yöntem adlarını temel alır. Ancak bazı senaryolarda kod oluşturma işlemi aşağıda açıklandığı gibi yeterli değildir:

  • Android, düzen XML özniteliklerinde eylem adlarını destekler; örneğin android:onClick XML özniteliği. Belirtildiğinde, şişirilmiş Görünüm örneği Java yöntemini aramaya çalışır.

  • java.io.Serializable arabirimi için readObject ve writeObject yöntemleri gerekir. Bunlar bu arabirimin üyesi olmadığından, ilgili yönetilen uygulamamız bu yöntemleri Java kodunda kullanıma sunmaz.

  • android.os.Parcelable arabirimi, bir uygulama sınıfının türünde Parcelable.Creatorstatik bir alana CREATOR sahip olmasını bekler. Oluşturulan Java kodu için açık bir alan gerekir. Standart senaryomuzda Java kodunda yönetilen koddan alan çıktısı vermenin bir yolu yoktur.

Kod oluşturma, Xamarin.Android 4.2'den başlayarak rastgele adlarla rastgele Java yöntemleri oluşturmaya yönelik bir çözüm sağlamadığından, yukarıdaki senaryolara bir çözüm sunmak için ExportAttribute ve ExportFieldAttribute kullanıma sunulmuştur. Her iki öznitelik de ad alanında Java.Interop bulunur:

  • ExportAttribute – bir yöntem adı ve beklenen özel durum türlerini belirtir (Java'da açık "throws" vermek için). Bir yöntemde kullanıldığında, yöntemi yönetilen yönteme karşılık gelen JNI çağrısına bir gönderme kodu oluşturan bir Java yöntemini "dışarı aktarır". Bu, ve java.io.Serializableile android:onClick kullanılabilir.

  • ExportFieldAttribute – bir alan adı belirtir. Alan başlatıcı olarak çalışan bir yöntemde bulunur. Bu, ile android.os.Parcelablekullanılabilir.

ExportAttribute ve ExportFieldAttribute sorunlarını giderme

  • Eksik Mono.Android.Export.dll nedeniyle paketleme başarısız oluyor; kodunuzda veya ExportFieldAttribute bağımlı kitaplıklarınızda veya bazı yöntemlerde kullandıysanızExportAttribute, Mono.Android.Export.dll eklemeniz gerekir. Bu derleme, Java'dan geri çağırma kodunu desteklemek için yalıtılır. Uygulamaya ek boyut eklediğinden Mono.Android.dll ayrıdır.

  • Yayın derlemesinde, MissingMethodException Dışarı Aktarma yöntemleri için gerçekleşir – Yayın derlemesinde, MissingMethodException Dışarı Aktarma yöntemleri için gerçekleşir. (Bu sorun, Xamarin.Android'in en son sürümünde düzeltildi.)

ExportParameterAttribute

ExportAttribute ve ExportFieldAttribute Java çalışma zamanı kodunun kullanabileceği işlevleri sağlar. Bu çalışma zamanı kodu, yönetilen koda bu öznitelikler tarafından yönlendirilen oluşturulan JNI yöntemleri aracılığıyla erişir. Sonuç olarak yönetilen yöntemin bağlamış olduğu bir Java yöntemi yoktur; bu nedenle, Java yöntemi yönetilen yöntem imzasından oluşturulur.

Ancak, bu durum tam olarak belirleyici değildir. En önemlisi, yönetilen türler ile Java türleri arasında aşağıdaki gibi bazı gelişmiş eşlemelerde bu geçerlidir:

  • Inputstream
  • Outputstream
  • XmlPullParser
  • XmlResourceParser

Dışarı aktarılan yöntemler için bunlar gibi türler gerektiğinde, ExportParameterAttribute ilgili parametreyi açıkça vermek veya bir tür döndürmek için kullanılmalıdır.

Ek Açıklama Özniteliği

Xamarin.Android 4.2'de uygulama türlerini özniteliklere (System.Attribute) dönüştürdük IAnnotation ve Java sarmalayıcılarında ek açıklama oluşturma desteği ekledik.

Bu, aşağıdaki yön değişiklikleri anlamına gelir:

  • Bağlama oluşturucudan java.Lang.Deprecated oluşturur Java.Lang.DeprecatedAttribute (yönetilen kodda olması [Obsolete] gerekirken).

  • Bu, mevcut Java.Lang.Deprecated sınıfın yok olacağı anlamına gelmez. Bu Java tabanlı nesneler hala normal Java nesneleri olarak kullanılabilir (bu tür bir kullanım varsa). Ve sınıfları olacak DeprecatedDeprecatedAttribute .

  • Java.Lang.DeprecatedAttribute sınıfı olarak [Annotation] işaretlenir. Bu [Annotation] öznitelikten devralınan özel bir öznitelik olduğunda, msbuild görevi Android Çağrılabilen Sarmalayıcı'da (ACW) bu özel öznitelik (@Deprecated) için bir Java ek açıklaması oluşturur.

  • Ek açıklamalar sınıflara, yöntemlere ve dışarı aktarılan alanlara oluşturulabilir (yönetilen koddaki bir yöntemdir).

İçeren sınıf (açıklama eklenen sınıfın kendisi veya açıklama eklenen üyeleri içeren sınıf) kayıtlı değilse, ek açıklamalar dahil olmak üzere java sınıf kaynağının tamamı hiç oluşturulmaz. Yöntemler için, yönteminin açıkça oluşturulmasını ve ek açıklama eklemesini almak için öğesini belirtebilirsiniz ExportAttribute . Ayrıca, Java ek açıklaması sınıf tanımını "oluşturmak" için bir özellik değildir. Başka bir deyişle, belirli bir ek açıklama için özel yönetilen öznitelik tanımlarsanız, karşılık gelen Java ek açıklama sınıfını içeren başka bir .jar kitaplığı eklemeniz gerekir. Ek açıklama türünü tanımlayan bir Java kaynak dosyası eklemek yeterli değildir. Java derleyicisi apt ile aynı şekilde çalışmaz.

Ayrıca aşağıdaki sınırlamalar geçerlidir:

  • Bu dönüştürme işlemi, şu ana kadar ek açıklama türüyle ilgili ek açıklamayı dikkate @Target almaz.

  • Bir özelliğe yönelik öznitelikler çalışmıyor. Bunun yerine özellik alıcısı veya ayarlayıcısı için öznitelikleri kullanın.

Sınıf Bağlama

Sınıfı bağlama, temel java türünün çağrılmayı kolaylaştırmak için yönetilen çağrılabilen sarmalayıcı yazma anlamına gelir.

C# ile geçersiz kılmaya izin vermek için sanal ve soyut yöntemlerin bağlanması için Xamarin.Android 4.0 gerekir. Ancak, Xamarin.Android'in herhangi bir sürümü, geçersiz kılmaları desteklemeden sanal olmayan yöntemleri, statik yöntemleri veya sanal yöntemleri bağlayabilir.

Bağlama genellikle aşağıdaki öğeleri içerir:

Tür Tanıtıcısı Bildiriyor

Alan ve yöntem arama yöntemleri, bildirim türüne başvuran bir nesne başvurusu gerektirir. Kural gereği, bu bir class_ref alanda tutulur:

static IntPtr class_ref = JNIEnv.FindClass(CLASS);

Belirteçle ilgili CLASS ayrıntılar için JNI Tür Başvuruları bölümüne bakın.

Alanları Bağlama

Java alanları C# özellikleri olarak sunulur, örneğin Java alanı java.lang.System.in C# özelliği Java.Lang.JavaSystem.In olarak bağlıdır. Ayrıca, JNI statik alanlar ve örnek alanları arasında ayrım yaptığınızdan, özellikler uygulanırken farklı yöntemler kullanılır.

Alan bağlama üç yöntem kümesi içerir:

  1. Get alan kimliği yöntemi. Get alan kimliği yöntemi, get alan değerinin ve alan değeri ayarlama yöntemlerinin kullanacağı bir alan tutamacını döndürmekten sorumludur. Alan kimliğini almak için bildirim türünü, alanın adını ve alanın JNI türü imzasını bilmek gerekir.

  2. Get alan değeri yöntemleri. Bu yöntemler alan tutamacını gerektirir ve alanın değerini Java'dan okumaktan sorumludur. Kullanılacak yöntem, alanın türüne bağlıdır.

  3. Set alan değeri yöntemleri. Bu yöntemler alan tutamacını gerektirir ve alanın değerini Java içinde yazmakla sorumludur. Kullanılacak yöntem, alanın türüne bağlıdır.

Statik alanlar JNIEnv.GetStaticFieldID, JNIEnv.GetStatic*Fieldve JNIEnv.SetStaticField yöntemlerini kullanır.

Örnek alanları JNIEnv.GetFieldID, JNIEnv.Get*Fieldve JNIEnv.SetField yöntemlerini kullanır.

Örneğin, statik özellik JavaSystem.In şu şekilde uygulanabilir:

static IntPtr in_jfieldID;
public static System.IO.Stream In
{
    get {
        if (in_jfieldId == IntPtr.Zero)
            in_jfieldId = JNIEnv.GetStaticFieldID (class_ref, "in", "Ljava/io/InputStream;");
        IntPtr __ret = JNIEnv.GetStaticObjectField (class_ref, in_jfieldId);
        return InputStreamInvoker.FromJniHandle (__ret, JniHandleOwnership.TransferLocalRef);
    }
}

Not: JNI başvuruyu bir System.IO.Stream örneğe dönüştürmek için InputStreamInvoker.FromJniHandle kullanıyoruz ve JNIEnv.GetStaticObjectField yerel başvuru döndürdüğü için kullanıyoruzJniHandleOwnership.TransferLocalRef.

Android.Runtime türlerinin FromJniHandle çoğunda JNI başvurularını istenen türe dönüştüren yöntemler bulunur.

Yöntem Bağlama

Java yöntemleri C# yöntemleri ve C# özellikleri olarak sunulur. Örneğin java.lang.Runtime.runFinalizersOnExit Java yöntemi Java.Lang.Runtime.RunFinalizersOnExit yöntemi ve java.lang.Object.getClass yöntemi java.Lang.Object.Class özelliği olarak bağlıdır.

Yöntem çağırma iki adımlı bir işlemdir:

  1. Çağıracak yöntemin get yöntemi kimliği . Get yöntemi kimlik yöntemi, yöntem çağırma yöntemlerinin kullanacağı bir yöntem tanıtıcısı döndürmekten sorumludur. Yöntem kimliğini almak için bildirim türünü, yöntemin adını ve yöntemin JNI türü imzasını bilmek gerekir.

  2. yöntemini çağırın.

Alanlarda olduğu gibi, yöntem kimliğini almak ve yöntemini çağırmak için kullanılacak yöntemler statik yöntemlerle örnek yöntemleri arasında farklılık gösterir.

Statik yöntemler JNIEnv.GetStaticMethodID() kullanarak yöntem kimliğini arar ve çağırma için yöntem ailesini kullanırJNIEnv.CallStatic*Method.

Örnek yöntemleri JNIEnv.GetMethodID kullanarak yöntem kimliğini arar ve çağırma için ve JNIEnv.CallNonvirtual*Method yöntemlerinin ailelerini kullanırJNIEnv.Call*Method.

Yöntem bağlama, olası olarak yalnızca yöntem çağırmadan daha fazlasıdır. Yöntem bağlama, bir yöntemin geçersiz kılınmasına (soyut ve son olmayan yöntemler için) veya uygulanmasına (arabirim yöntemleri için) izin verme de içerir. Destekleyici Devralma, Arabirimler bölümü, destekleyici sanal yöntemlerin ve arabirim yöntemlerinin karmaşıklıklarını kapsar.

Statik Yöntemler

Statik yöntemin bağlanması, yöntemin JNIEnv.GetStaticMethodID dönüş türüne bağlı olarak yöntemin tanıtıcısını elde etmek için komutunu, ardından uygun JNIEnv.CallStatic*Method yöntemi kullanmayı içerir. Runtime.getRuntime yöntemi için bağlama örneği aşağıda verilmiştir:

static IntPtr id_getRuntime;

[Register ("getRuntime", "()Ljava/lang/Runtime;", "")]
public static Java.Lang.Runtime GetRuntime ()
{
    if (id_getRuntime == IntPtr.Zero)
        id_getRuntime = JNIEnv.GetStaticMethodID (class_ref,
                "getRuntime", "()Ljava/lang/Runtime;");

    return Java.Lang.Object.GetObject<Java.Lang.Runtime> (
            JNIEnv.CallStaticObjectMethod  (class_ref, id_getRuntime),
            JniHandleOwnership.TransferLocalRef);
}

Yöntem tanıtıcısını statik bir alanda id_getRuntimedepoladığımıza dikkat edin. Bu bir performans iyileştirmesi olduğundan, yöntem tanıtıcısının her çağrıda aranması gerekmez. Yöntem tanıtıcısını bu şekilde önbelleğe almak gerekli değildir. Yöntem tanıtıcısı elde edildikten sonra, yöntemini çağırmak için JNIEnv.CallStaticObjectMethod kullanılır. JNIEnv.CallStaticObjectMethod döndürülen Java örneğinin tutamacını içeren bir IntPtr döndürür. Java.Lang.Object.GetObject<T>(IntPtr, JniHandleOwnership), Java tutamacını kesin olarak belirlenmiş bir nesne örneğine dönüştürmek için kullanılır.

Sanal Olmayan Örnek Yöntemi Bağlama

final Bir örnek yönteminin veya geçersiz kılma gerektirmeyen bir örnek yönteminin bağlanması, yöntemin dönüş türüne bağlı olarak bir yöntem tutamacını almak için kullanmayı ve ardından uygun JNIEnv.Call*Method yöntemi kullanmayı içerirJNIEnv.GetMethodID. Aşağıda özelliği için bağlama örneği verilmiştir Object.Class :

static IntPtr id_getClass;
public Java.Lang.Class Class {
    get {
        if (id_getClass == IntPtr.Zero)
            id_getClass = JNIEnv.GetMethodID (class_ref, "getClass", "()Ljava/lang/Class;");
        return Java.Lang.Object.GetObject<Java.Lang.Class> (
                JNIEnv.CallObjectMethod (Handle, id_getClass),
                JniHandleOwnership.TransferLocalRef);
    }
}

Yöntem tanıtıcısını statik bir alanda id_getClassdepoladığımıza dikkat edin. Bu bir performans iyileştirmesi olduğundan, yöntem tanıtıcısının her çağrıda aranması gerekmez. Yöntem tanıtıcısını bu şekilde önbelleğe almak gerekli değildir. Yöntem tanıtıcısı elde edildikten sonra, yöntemini çağırmak için JNIEnv.CallStaticObjectMethod kullanılır. JNIEnv.CallStaticObjectMethod döndürülen Java örneğinin tutamacını içeren bir IntPtr döndürür. Java.Lang.Object.GetObject<T>(IntPtr, JniHandleOwnership), Java tutamacını kesin olarak belirlenmiş bir nesne örneğine dönüştürmek için kullanılır.

Bağlama Oluşturucuları

Oluşturucular, adlı "<init>"Java yöntemleridir. Aynı Java örnek yöntemlerinde olduğu gibi oluşturucu JNIEnv.GetMethodID tutamacını aramada da kullanılır. Java yöntemlerinden farklı olarak, oluşturucu yöntemi tanıtıcısını çağırmak için JNIEnv.NewObject yöntemleri kullanılır. değerinin JNIEnv.NewObject dönüş değeri bir JNI yerel başvurusudur:

int value = 42;
IntPtr class_ref    = JNIEnv.FindClass ("java/lang/Integer");
IntPtr id_ctor_I    = JNIEnv.GetMethodID (class_ref, "<init>", "(I)V");
IntPtr lrefInstance = JNIEnv.NewObject (class_ref, id_ctor_I, new JValue (value));
// Dispose of lrefInstance, class_ref…

Normalde bir sınıf bağlaması Java.Lang.Object dosyasını alt sınıflandırır. alt sınıflama sırasında Java.Lang.Objectek bir semantik devreye girer: örnek Java.Lang.Object , özelliği aracılığıyla Java.Lang.Object.Handle bir Java örneğine genel bir başvuru tutar.

  1. Varsayılan Java.Lang.Object oluşturucu bir Java örneği ayırır.

  2. Türün bir RegisterAttributeRegisterAttribute.DoNotGenerateAcw ve ise true , türün RegisterAttribute.Name bir örneği varsayılan oluşturucu aracılığıyla oluşturulur.

  3. Aksi takdirde, öğesine karşılık gelen this.GetType Android Çağrılabilen Sarmalayıcı (ACW) varsayılan oluşturucu aracılığıyla örneği oluşturulur. Android Çağrılabilen Sarmalayıcılar, için olarak ayarlanmayan RegisterAttribute.DoNotGenerateAcwtrueher Java.Lang.Object alt sınıf için paket oluşturma sırasında oluşturulur.

Sınıf bağlaması olmayan türler için beklenen semantik budur: C Mono.Samples.HelloWorld.HelloAndroid # örneğinin örneği oluşturulurken, oluşturulan bir Android Çağrılabilen Sarmalayıcı olan bir Java mono.samples.helloworld.HelloAndroid örneği oluşturulmalıdır.

Java türü varsayılan bir oluşturucu içeriyorsa ve/veya başka bir oluşturucunun çağrılması gerekmiyorsa, sınıf bağlamaları için bu doğru davranış olabilir. Aksi takdirde, aşağıdaki eylemleri gerçekleştiren bir oluşturucu sağlanmalıdır:

  1. Varsayılan Java.Lang.Object oluşturucu yerine Java.Lang.Object(IntPtr, JniHandleOwnership) çağrılır. Bu, yeni bir Java örneği oluşturmamak için gereklidir.

  2. Java örneği oluşturmadan önce Java.Lang.Object.Handle değerini denetleyin. Özelliği, Object.Handle Java kodunda Bir Android Çağrılabilen Sarmalayıcı oluşturulup oluşturulmaması dışında IntPtr.Zero bir değere sahip olur ve sınıf bağlaması oluşturulan Android Çağrılabilen Sarmalayıcı örneğini içerecek şekilde oluşturulur. Örneğin, Android bir mono.samples.helloworld.HelloAndroid örnek oluşturduğunda, önce Android Çağrılabilen Sarmalayıcı oluşturulur ve Java HelloAndroid oluşturucu, oluşturucu yürütmeden önce özelliği Java örneğine ayarlanmış şekilde Object.Handle ilgili Mono.Samples.HelloWorld.HelloAndroid türün bir örneğini oluşturur.

  3. Geçerli çalışma zamanı türü bildirim türüyle aynı değilse, ilgili Android Çağrılabilen Sarmalayıcı örneği oluşturulmalı ve JNIEnv.CreateInstance tarafından döndürülen tanıtıcıyı depolamak için Object.SetHandle kullanılmalıdır.

  4. Geçerli çalışma zamanı türü bildirim türüyle aynıysa, Java oluşturucuyu çağırın ve tarafından JNIEnv.NewInstance döndürülen tanıtıcıyı depolamak için Object.SetHandle kullanın.

Örneğin, java.lang.Integer(int) oluşturucuyu göz önünde bulundurun. Bu, şu şekilde bağlıdır:

// Cache the constructor's method handle for later use
static IntPtr id_ctor_I;

// Need [Register] for subclassing
// RegisterAttribute.Name is always ".ctor"
// RegisterAttribute.Signature is tye JNI type signature of constructor
// RegisterAttribute.Connector is ignored; use ""
[Register (".ctor", "(I)V", "")]
public Integer (int value)
    // 1. Prevent Object default constructor execution
    : base (IntPtr.Zero, JniHandleOwnership.DoNotTransfer)
{
    // 2. Don't allocate Java instance if already allocated
    if (Handle != IntPtr.Zero)
        return;

    // 3. Derived type? Create Android Callable Wrapper
    if (GetType () != typeof (Integer)) {
        SetHandle (
                Android.Runtime.JNIEnv.CreateInstance (GetType (), "(I)V", new JValue (value)),
                JniHandleOwnership.TransferLocalRef);
        return;
    }

    // 4. Declaring type: lookup &amp; cache method id...
    if (id_ctor_I == IntPtr.Zero)
        id_ctor_I = JNIEnv.GetMethodID (class_ref, "<init>", "(I)V");
    // ...then create the Java instance and store
    SetHandle (
            JNIEnv.NewObject (class_ref, id_ctor_I, new JValue (value)),
            JniHandleOwnership.TransferLocalRef);
}

JNIEnv.CreateInstance yöntemleri, ' den JNIEnv.FindClassdöndürülen değer üzerinde , JNIEnv.GetMethodID, JNIEnv.NewObjectve JNIEnv.DeleteGlobalReference gerçekleştirmek JNIEnv.FindClassiçin yardımcıdır. Ayrıntılar için sonraki bölüme bakın.

Devralmayı Destekleme, Arabirimler

Java türünün alt sınıflanması veya Java arabiriminin uygulanması, paketleme işlemi sırasında her Java.Lang.Object alt sınıf için oluşturulan Android Çağrılabilen Sarmalayıcıların (ACW) oluşturulmasını gerektirir. ACW oluşturma, Android.Runtime.RegisterAttribute özel özniteliği aracılığıyla denetlenür.

C# türleri için özel [Register] öznitelik oluşturucu tek bir bağımsız değişken gerektirir: karşılık gelen Java türü için JNI basitleştirilmiş tür başvurusu . Bu, Java ile C# arasında farklı adlar sağlamaya olanak tanır.

Xamarin.Android 4.0'dan önce, [Register] özel öznitelik mevcut Java türlerinde "diğer ad" kullanmıyordu. Bunun nedeni, ACW oluşturma işleminin karşılaşılan her Java.Lang.Object alt sınıf için ACW oluşturmasıdır.

Xamarin.Android 4.0, RegisterAttribute.DoNotGenerateAcw özelliğini kullanıma sunar. Bu özellik, ACW oluşturma işlemine açıklamalı türü atlayarak paket oluşturma zamanında ACW'lerin oluşturulmasına neden olmayacak yeni Yönetilen Çağrılabilen Sarmalayıcılar bildirimine izin verir. Bu, mevcut Java türlerini bağlamaya olanak tanır. Örneğin, Addertamsayılara ekleyip sonucu döndüren bir yöntem addiçeren aşağıdaki basit Java sınıfını göz önünde bulundurun:

package mono.android.test;
public class Adder {
    public int add (int a, int b) {
        return a + b;
    }
}

Tür Adder şu şekilde bağlanabilir:

[Register ("mono/android/test/Adder", DoNotGenerateAcw=true)]
public partial class Adder : Java.Lang.Object {
    static IntPtr class_ref = JNIEnv.FindClass ( "mono/android/test/Adder");

    public Adder ()
    {
    }

    public Adder (IntPtr handle, JniHandleOwnership transfer)
        : base (handle, transfer)
    {
    }
}
partial class ManagedAdder : Adder {
}

Burada CAdder# türü Java türüne Adder diğer adlar ekler. [Register] özniteliği Java türünün JNI adını mono.android.test.Adder belirtmek için kullanılır ve DoNotGenerateAcw özelliği acw oluşturmayı engellemek için kullanılır. Bu, türün düzgün alt sınıflarına sahip bir ACW ManagedAdder oluşturulmasına mono.android.test.Adder neden olur. RegisterAttribute.DoNotGenerateAcw Özellik kullanılmamış olsaydı, Xamarin.Android derleme işlemi yeni mono.android.test.Adder bir Java türü oluştururdu. Bu, mono.android.test.Adder tür iki ayrı dosyada iki kez mevcut olacağından derleme hatalarına neden olur.

Sanal Yöntemleri Bağlama

ManagedAdder Java Adder türünü alt sınıflar, ancak özellikle ilginç değildir: C# Adder türü herhangi bir sanal yöntem tanımlamaz, bu nedenle ManagedAdder hiçbir şeyi geçersiz kılamaz.

Alt sınıflar tarafından geçersiz kılınmaya izin vermek için bağlama virtual yöntemleri, aşağıdaki iki kategoriye giren birkaç işlem yapılmasını gerektirir:

  1. Yöntem Bağlama

  2. Yöntem Kaydı

Yöntem Bağlama

Yöntem bağlama, C# Adder tanımına iki destek üyesi eklenmesini gerektirir: ThresholdType, ve ThresholdClass.

ThresholdType

ThresholdType özelliği bağlamanın geçerli türünü döndürür:

partial class Adder {
    protected override System.Type ThresholdType {
        get {
            return typeof (Adder);
        }
    }
}

ThresholdType , sanal ve sanal olmayan yöntem gönderimini ne zaman gerçekleştirmesi gerektiğini belirlemek için Yöntem Bağlaması'nda kullanılır. Her zaman bildirimde bulunan C# türüne karşılık gelen bir System.Type örnek döndürmelidir.

ThresholdClass

özelliği, ThresholdClass ilişkili tür için JNI sınıf başvuruyu döndürür:

partial class Adder {
    protected override IntPtr ThresholdClass {
        get {
            return class_ref;
        }
    }
}

ThresholdClass sanal olmayan yöntemler çağrılırken Yöntem Bağlama'da kullanılır.

Bağlama Uygulaması

Yöntem bağlama uygulaması Java yönteminin çalışma zamanı çağrısından sorumludur. Ayrıca, yöntem kaydının parçası olan özel bir [Register] öznitelik bildirimi içerir ve Yöntem Kaydı bölümünde ele alınacaktır:

[Register ("add", "(II)I", "GetAddHandler")]
    public virtual int Add (int a, int b)
    {
        if (id_add == IntPtr.Zero)
            id_add = JNIEnv.GetMethodID (class_ref, "add", "(II)I");
        if (GetType () == ThresholdType)
            return JNIEnv.CallIntMethod (Handle, id_add, new JValue (a), new JValue (b));
        return JNIEnv.CallNonvirtualIntMethod (Handle, ThresholdClass, id_add, new JValue (a), new JValue (b));
    }
}

alanı id_add , çağrılacak Java yönteminin yöntem kimliğini içerir. id_add değeri, bildirim sınıfını (), Java yöntemi adını (class_ref ve yönteminin JNI imzasını ("add""(II)I" gerektiren öğesinden JNIEnv.GetMethodIDelde edilir.

Yöntem kimliği alındıktan sonra, GetType sanal veya sanal olmayan gönderimin gerekli olup olmadığını belirlemek için ThresholdType karşılaştırılır. Yöntemi geçersiz kılan Java tarafından ayrılmış bir alt sınıfa başvurabileceği gibiHandle, ile eşleştiğinde GetTypeThresholdTypesanal dağıtım gereklidir.

ile eşleşmediğinde GetTypeThresholdTypeAdder alt sınıfa (örneğin tarafındanManagedAdder) alınır ve Adder.Add uygulama yalnızca alt sınıf tarafından çağrıldığında çağrılır.base.Add Bu, sanal olmayan dağıtım olayıdır ve burada ThresholdClass devreye girer. ThresholdClass hangi Java sınıfının çağırmak için yönteminin uygulanmasını sağlayacağını belirtir.

Yöntem Kaydı

Yöntemini geçersiz kılan Adder.Add güncelleştirilmiş ManagedAdder bir tanımımız olduğunu varsayalım:

partial class ManagedAdder : Adder {
    public override int Add (int a, int b) {
        return (a*2) + (b*2);
    }
}

Özel bir [Register] özniteliği olduğunu Adder.Add hatırlayın:

[Register ("add", "(II)I", "GetAddHandler")]

[Register] Özel öznitelik oluşturucu üç değer kabul eder:

  1. Bu örnekte Java yönteminin "add" adı.

  2. Bu örnekte yönteminin "(II)I" JNI Tür İmzası.

  3. Bağlayıcı yöntemiGetAddHandler, bu örnekte. Bağlan veya yöntemleri daha sonra ele alınacaktır.

İlk iki parametre, ACW oluşturma işleminin yöntemi geçersiz kılmak için bir yöntem bildirimi oluşturmasına olanak sağlar. Sonuçta elde edilen ACW aşağıdaki kodlardan bazılarını içerebilir:

public class ManagedAdder extends mono.android.test.Adder {
    static final String __md_methods;
    static {
        __md_methods = "n_add:(II)I:GetAddHandler\n" +
            "";
        mono.android.Runtime.register (...);
    }
    @Override
    public int add (int p0, int p1) {
        return n_add (p0, p1);
    }
    private native int n_add (int p0, int p1);
    // ...
}

Bir yöntemin @Override bildirildiğini ve bu yöntemin aynı ada sahip ön ekli bir n_yönteme temsilci olarak atandığını unutmayın. Bu, Java kodu çağrıldığında ManagedAdder.addManagedAdder.n_add C# ManagedAdder.Add yönteminin yürütülmesini sağlayacak şekilde çağrılmasını sağlar.

Bu nedenle, en önemli soru: nasıl ManagedAdder.n_add bağlı ManagedAdder.Add?

Java native yöntemleri, JNI RegisterNatives işlevi aracılığıyla Java (Android çalışma zamanı) çalışma zamanına kaydedilir. RegisterNativesJava yöntemi adını, JNI Tür İmzasını ve JNI çağırma kuralına göre çağıracak bir işlev işaretçisini içeren bir yapı dizisi alır. İşlev işaretçisi, iki işaretçi bağımsız değişkenini ve ardından yöntem parametrelerini alan bir işlev olmalıdır. Java ManagedAdder.n_add yöntemi aşağıdaki C prototipini içeren bir işlev aracılığıyla uygulanmalıdır:

int FunctionName(JNIEnv *env, jobject this, int a, int b)

Xamarin.Android bir RegisterNatives yöntemi kullanıma sunmaz. Bunun yerine, ACW ve MCW birlikte çağırmak RegisterNativesiçin gerekli bilgileri sağlar: ACW yöntem adını ve JNI tür imzasını içerir, eksik olan tek şey bağlanmaya yönelik bir işlev işaretçisidir.

Bağlayıcı yöntemi burada devreye girer. Üçüncü [Register] özel öznitelik parametresi, kayıtlı türde tanımlanan bir yöntemin adı veya hiçbir parametre kabul edip döndüren System.Delegatekayıtlı türün temel sınıfıdır. Sırayla döndürülen System.Delegate , doğru JNI işlev imzasına sahip bir yönteme başvurur. Son olarak, bağlayıcı yönteminin döndürdüğü temsilcinin kök kökünü alması gerekir , böylece gc bunu toplamaz, çünkü temsilci Java'ya sağlanır.

#pragma warning disable 0169
static Delegate cb_add;
// This method must match the third parameter of the [Register]
// custom attribute, must be static, must return System.Delegate,
// and must accept no parameters.
static Delegate GetAddHandler ()
{
    if (cb_add == null)
        cb_add = JNINativeWrapper.CreateDelegate ((Func<IntPtr, IntPtr, int, int, int>) n_Add);
    return cb_add;
}
// This method is registered with JNI.
static int n_Add (IntPtr jnienv, IntPtr lrefThis, int a, int b)
{
    Adder __this = Java.Lang.Object.GetObject<Adder>(lrefThis, JniHandleOwnership.DoNotTransfer);
    return __this.Add (a, b);
}
#pragma warning restore 0169

yöntemi yöntemine GetAddHandlern_Add başvuran bir Func<IntPtr, IntPtr, int, int, int> temsilci oluşturur ve ardından JNINativeWrapper.CreateDelegate öğesini çağırır. JNINativeWrapper.CreateDelegatesağlanan yöntemi bir try/catch bloğuna sarmalar, böylece işlenmeyen özel durumlar işlenir ve AndroidEvent.UnhandledExceptionRaiser olayının oluşturulmasına neden olur. Elde edilen temsilci, GC'nin temsilciyi serbest bırakmaması için statik cb_add değişkende depolanır.

Son olarak, n_Add yöntemi JNI parametrelerini ilgili yönetilen türlere göre sıralamaktan ve ardından yöntem çağrısına temsilci seçmekle sorumludur.

Not: Java örneği üzerinden MCW elde ederken her zaman kullanın JniHandleOwnership.DoNotTransfer . Bunları yerel bir başvuru (ve dolayısıyla çağırma) olarak ele JNIEnv.DeleteLocalRefalırsa yönetilen -> Java -> yönetilen yığın geçişleri bozulacaktır.

Tam Eklenti Bağlaması

Türün tam yönetilen bağlaması mono.android.tests.Adder :

[Register ("mono/android/test/Adder", DoNotGenerateAcw=true)]
public class Adder : Java.Lang.Object {

    static IntPtr class_ref = JNIEnv.FindClass ("mono/android/test/Adder");

    public Adder ()
    {
    }

    public Adder (IntPtr handle, JniHandleOwnership transfer)
        : base (handle, transfer)
    {
    }

    protected override Type ThresholdType {
        get {return typeof (Adder);}
    }

    protected override IntPtr ThresholdClass {
        get {return class_ref;}
    }

#region Add
    static IntPtr id_add;

    [Register ("add", "(II)I", "GetAddHandler")]
    public virtual int Add (int a, int b)
    {
        if (id_add == IntPtr.Zero)
            id_add = JNIEnv.GetMethodID (class_ref, "add", "(II)I");
        if (GetType () == ThresholdType)
            return JNIEnv.CallIntMethod (Handle, id_add, new JValue (a), new JValue (b));
        return JNIEnv.CallNonvirtualIntMethod (Handle, ThresholdClass, id_add, new JValue (a), new JValue (b));
    }

#pragma warning disable 0169
    static Delegate cb_add;
    static Delegate GetAddHandler ()
    {
        if (cb_add == null)
            cb_add = JNINativeWrapper.CreateDelegate ((Func<IntPtr, IntPtr, int, int, int>) n_Add);
        return cb_add;
    }

    static int n_Add (IntPtr jnienv, IntPtr lrefThis, int a, int b)
    {
        Adder __this = Java.Lang.Object.GetObject<Adder>(lrefThis, JniHandleOwnership.DoNotTransfer);
        return __this.Add (a, b);
    }
#pragma warning restore 0169
#endregion
}

Kısıtlamalar

Aşağıdaki ölçütlere uyan bir tür yazarken:

  1. Alt Java.Lang.Object

  2. Özel bir [Register] özniteliği var

  3. RegisterAttribute.DoNotGenerateAcwtrue

Daha sonra GC etkileşimi için türün çalışma zamanında bir veya Java.Lang.Object alt sınıfına başvurabilecek herhangi bir Java.Lang.Object alanı olmamalıdır. Örneğin, tür System.Object ve arabirim türü alanlara izin verilmez. Ve List<int>gibi System.String örneklere Java.Lang.Object başvuramayan türlere izin verilir. Bu kısıtlama GC tarafından erken nesne toplamayı önlemektir.

Türün örneğe başvurabilen Java.Lang.Object bir örnek alanı içermesi gerekiyorsa, alan türü veya GCHandleolmalıdırSystem.WeakReference.

Soyut Yöntemleri Bağlama

Bağlama abstract yöntemleri büyük ölçüde sanal yöntemleri bağlama ile aynıdır. Yalnızca iki fark vardır:

  1. Soyut yöntem soyut. Özniteliğini [Register] ve ilişkili Yöntem Kaydını korur, Yöntem Bağlama türüne Invoker yeni taşınır.

  2. Soyut türü alt sınıflayan tür olmayan abstractInvoker bir oluşturulur. Türün Invoker temel sınıfta bildirilen tüm soyut yöntemleri geçersiz kılması gerekir ve geçersiz kılınan uygulama Yöntem Bağlama uygulamasıdır, ancak sanal olmayan dağıtım durumu yoksayılabilir.

Örneğin, yukarıdaki mono.android.test.Adder.add yöntemin olduğunu abstractvarsayalım. C# bağlaması soyut olacak şekilde Adder.Add değişir ve uygulayan Adder.Addyeni AdderInvoker bir tür tanımlanır:

partial class Adder {
    [Register ("add", "(II)I", "GetAddHandler")]
    public abstract int Add (int a, int b);

    // The Method Registration machinery is identical to the
    // virtual method case...
}

partial class AdderInvoker : Adder {
    public AdderInvoker (IntPtr handle, JniHandleOwnership transfer)
        : base (handle, transfer)
    {
    }

    static IntPtr id_add;
    public override int Add (int a, int b)
    {
        if (id_add == IntPtr.Zero)
            id_add = JNIEnv.GetMethodID (class_ref, "add", "(II)I");
        return JNIEnv.CallIntMethod (Handle, id_add, new JValue (a), new JValue (b));
    }
}

Türü Invoker yalnızca Java tarafından oluşturulan örneklere JNI başvuruları alınırken gereklidir.

Arabirimleri Bağlama

Bağlama arabirimleri kavramsal olarak sanal yöntemler içeren bağlama sınıflarına benzer, ancak özelliklerin çoğu ince (çok ince değil) yollarla farklılık gösterir. Aşağıdaki Java arabirim bildirimini göz önünde bulundurun:

public interface Progress {
    void onAdd(int[] values, int currentIndex, int currentSum);
}

Arabirim bağlamalarının iki bölümü vardır: C# arabirim tanımı ve arabirim için bir Invoker tanımı.

Arabirim Tanımı

C# arabirim tanımı aşağıdaki gereksinimleri karşılamalıdır:

  • Arabirim tanımının özel bir [Register] özniteliği olmalıdır.

  • Arabirim tanımının kapsamını genişletmesi IJavaObject interfacegerekir. Bunun yapılmaması, ACW'lerin Java arabiriminden devralmasını engeller.

  • Her arabirim yöntemi, karşılık gelen Java yöntemi adını, JNI imzasını ve bağlayıcı yöntemini belirten bir [Register] öznitelik içermelidir.

  • Bağlayıcı yöntemi, bağlayıcı yönteminin bulunabileceği türü de belirtmelidir.

Bağlama abstract ve virtual yöntemler sırasında bağlayıcı yöntemi, kaydedilmekte olan türün devralma hiyerarşisinde aranırdı. Arabirimler gövde içeren hiçbir yönteme sahip olmayabilir, bu nedenle bu işe yaramaz, bu nedenle bağlayıcı yönteminin bulunduğu yeri belirten bir tür belirtilmesi gereksinimidir. Türü, bağlayıcı yöntem dizesi içinde iki nokta üst üste ':'sonra belirtilir ve çağırıcıyı içeren türün derleme türü adı olmalıdır.

Arabirim yöntemi bildirimleri, uyumlu türler kullanılarak karşılık gelen Java yönteminin çevirisidir. Java yerleşik türleri için uyumlu türler karşılık gelen C# türleridir; örneğin Java int , C# intşeklindedir. Başvuru türleri için uyumlu tür, uygun Java türünde bir JNI tanıtıcısı sağlayabilen bir türdür.

Arabirim üyeleri Java tarafından doğrudan çağrılmayacak; çağırmaya Invoker türü aracılığıyla aracılık edilecek, bu nedenle bir miktar esnekliğe izin verilir.

Java İlerleme Durumu arabirimi C# dilinde şu şekilde bildirilebilir:

[Register ("mono/android/test/Adder$Progress", DoNotGenerateAcw=true)]
public interface IAdderProgress : IJavaObject {
    [Register ("onAdd", "([III)V",
            "GetOnAddHandler:Mono.Samples.SanityTests.IAdderProgressInvoker, SanityTests, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")]
    void OnAdd (JavaArray<int> values, int currentIndex, int currentSum);
}

Yukarıdakilerde Java parametresini bir JavaArray int[]<int> ile eşlediğimize dikkat edin. Bu gerekli değildir: Bunu bir C# int[]veya bir veya tamamen başka bir IList<int>şeye bağlamış olabilirdik. Hangi tür seçilirse seçilsin, Invoker çağrı için java int[] türüne çevirebilmesi gerekir.

Invoker Tanımı

Tür tanımının Invoker devralması Java.Lang.Object, uygun arabirimi uygulaması ve arabirim tanımında başvuruda bulunılan tüm bağlantı yöntemlerini sağlaması gerekir. Sınıf bağlamasından farklı bir öneri daha vardır: class_ref alan ve yöntem kimlikleri statik üyeler değil örnek üyeleri olmalıdır.

Örnek üyelerini tercih etme nedeni, Android çalışma zamanındaki davranışla JNIEnv.GetMethodID ilişkilidir. (Bu java davranışı da olabilir; test edilmemiştir.) JNIEnv.GetMethodID , bildirilen arabirimden değil, uygulanan arabirimden gelen bir yöntemi ararken null döndürür. java.util.Map K, V> arabirimini uygulayan java.util.SortedMap<<K, V> Java arabirimini düşünün. Map net bir yöntem sağlar, bu nedenle SortedMap için makul görünen Invoker bir tanım şöyle olur:

// Fails at runtime. DO NOT FOLLOW
partial class ISortedMapInvoker : Java.Lang.Object, ISortedMap {
    static IntPtr class_ref = JNIEnv.FindClass ("java/util/SortedMap");
    static IntPtr id_clear;
    public void Clear()
    {
        if (id_clear == IntPtr.Zero)
            id_clear = JNIEnv.GetMethodID(class_ref, "clear", "()V");
        JNIEnv.CallVoidMethod(Handle, id_clear);
    }
     // ...
}

Yukarıdaki başarısız olur çünkü JNIEnv.GetMethodID sınıf örneği aracılığıyla SortedMap yöntemi ararken Map.clear döndürülecektirnull.

Bunun iki çözümü vardır: her yöntemin hangi arabirimden geldiğini ve her arabirim için bir class_ref arabirimi olduğunu izleyin veya her şeyi örnek üyesi olarak tutun ve yöntem aramasını arabirim türü yerine en çok türetilen sınıf türünde gerçekleştirin. İkincisi Mono.Android.dll yapılır.

Invoker tanımının altı bölümü vardır: oluşturucu, yöntem, DisposeThresholdType ve ThresholdClass üyeleri, GetObject yöntem, arabirim yöntemi uygulaması ve bağlayıcı yöntemi uygulaması.

Oluşturucu

Oluşturucunun çağrılan örneğin çalışma zamanı sınıfını araması ve çalışma zamanı sınıfını örnek class_ref alanında depolaması gerekir:

partial class IAdderProgressInvoker {
    IntPtr class_ref;
    public IAdderProgressInvoker (IntPtr handle, JniHandleOwnership transfer)
        : base (handle, transfer)
    {
        IntPtr lref = JNIEnv.GetObjectClass (Handle);
        class_ref   = JNIEnv.NewGlobalRef (lref);
        JNIEnv.DeleteLocalRef (lref);
    }
}

Not: Handle Özellik, Android v4.0'da handle olduğu gibi, temel oluşturucu yürütmeyi tamamladıktan sonra parametre geçersiz olabilir ve parametrenin değil handle oluşturucu gövdesinde kullanılmalıdır.

Dispose Yöntemi

yönteminin Dispose oluşturucuda ayrılan genel başvuruyu boşaltması gerekir:

partial class IAdderProgressInvoker {
    protected override void Dispose (bool disposing)
    {
        if (this.class_ref != IntPtr.Zero)
            JNIEnv.DeleteGlobalRef (this.class_ref);
        this.class_ref = IntPtr.Zero;
        base.Dispose (disposing);
    }
}

ThresholdType ve ThresholdClass

ThresholdType ve ThresholdClass üyeleri, bir sınıf bağlamasında bulunanla aynıdır:

partial class IAdderProgressInvoker {
    protected override Type ThresholdType {
        get {
            return typeof (IAdderProgressInvoker);
        }
    }
    protected override IntPtr ThresholdClass {
        get {
            return class_ref;
        }
    }
}

GetObject Metodu

Extensions.JavaCast<T>()'yi desteklemek için statik GetObject bir yöntem gereklidir:

partial class IAdderProgressInvoker {
    public static IAdderProgress GetObject (IntPtr handle, JniHandleOwnership transfer)
    {
        return new IAdderProgressInvoker (handle, transfer);
    }
}

Arabirim Yöntemleri

Arabirimin her yönteminin JNI aracılığıyla karşılık gelen Java yöntemini çağıran bir uygulaması olması gerekir:

partial class IAdderProgressInvoker {
    IntPtr id_onAdd;
    public void OnAdd (JavaArray<int> values, int currentIndex, int currentSum)
    {
        if (id_onAdd == IntPtr.Zero)
            id_onAdd = JNIEnv.GetMethodID (class_ref, "onAdd", "([III)V");
        JNIEnv.CallVoidMethod (Handle, id_onAdd, new JValue (JNIEnv.ToJniHandle (values)), new JValue (currentIndex), new JValue (currentSum));
    }
}

Bağlan or Yöntemleri

Bağlayıcı yöntemleri ve destekleyici altyapı, JNI parametrelerini uygun C# türlerine göre sıralamaktan sorumludur. Java int[] parametresi, C# içinde bir IntPtr JNI jintArrayolarak geçirilir. IntPtr C# arabiriminin çağrılması desteklenebilmesi için JavaArray<int> olarak sıralanmalıdır:

partial class IAdderProgressInvoker {
    static Delegate cb_onAdd;
    static Delegate GetOnAddHandler ()
    {
        if (cb_onAdd == null)
            cb_onAdd = JNINativeWrapper.CreateDelegate ((Action<IntPtr, IntPtr, IntPtr, int, int>) n_OnAdd);
        return cb_onAdd;
    }

    static void n_OnAdd (IntPtr jnienv, IntPtr lrefThis, IntPtr values, int currentIndex, int currentSum)
    {
        IAdderProgress __this = Java.Lang.Object.GetObject<IAdderProgress>(lrefThis, JniHandleOwnership.DoNotTransfer);
        using (var _values = new JavaArray<int>(values, JniHandleOwnership.DoNotTransfer)) {
            __this.OnAdd (_values, currentIndex, currentSum);
        }
    }
}

yerine JavaList<int>tercih edilirse int[] JNIEnv.GetArray() kullanılabilir:

int[] _values = (int[]) JNIEnv.GetArray(values, JniHandleOwnership.DoNotTransfer, typeof (int));

Ancak, bu işlemin tüm diziyi VM'ler arasında kopyalandığını JNIEnv.GetArray , bu nedenle büyük diziler için bunun çok fazla gc baskısı oluşturabileceğini unutmayın.

Tam Çağırıcı Tanımı

IAdderProgressInvoker tanımının tamamı:

class IAdderProgressInvoker : Java.Lang.Object, IAdderProgress {

    IntPtr class_ref;

    public IAdderProgressInvoker (IntPtr handle, JniHandleOwnership transfer)
        : base (handle, transfer)
    {
        IntPtr lref = JNIEnv.GetObjectClass (Handle);
        class_ref = JNIEnv.NewGlobalRef (lref);
        JNIEnv.DeleteLocalRef (lref);
    }

    protected override void Dispose (bool disposing)
    {
        if (this.class_ref != IntPtr.Zero)
            JNIEnv.DeleteGlobalRef (this.class_ref);
        this.class_ref = IntPtr.Zero;
        base.Dispose (disposing);
    }

    protected override Type ThresholdType {
        get {return typeof (IAdderProgressInvoker);}
    }

    protected override IntPtr ThresholdClass {
        get {return class_ref;}
    }

    public static IAdderProgress GetObject (IntPtr handle, JniHandleOwnership transfer)
    {
        return new IAdderProgressInvoker (handle, transfer);
    }

#region OnAdd
    IntPtr id_onAdd;
    public void OnAdd (JavaArray<int> values, int currentIndex, int currentSum)
    {
        if (id_onAdd == IntPtr.Zero)
            id_onAdd = JNIEnv.GetMethodID (class_ref, "onAdd",
                    "([III)V");
        JNIEnv.CallVoidMethod (Handle, id_onAdd,
                new JValue (JNIEnv.ToJniHandle (values)),
                new JValue (currentIndex),
new JValue (currentSum));
    }

#pragma warning disable 0169
    static Delegate cb_onAdd;
    static Delegate GetOnAddHandler ()
    {
        if (cb_onAdd == null)
            cb_onAdd = JNINativeWrapper.CreateDelegate ((Action<IntPtr, IntPtr, IntPtr, int, int>) n_OnAdd);
        return cb_onAdd;
    }

    static void n_OnAdd (IntPtr jnienv, IntPtr lrefThis, IntPtr values, int currentIndex, int currentSum)
    {
        IAdderProgress __this = Java.Lang.Object.GetObject<IAdderProgress>(lrefThis, JniHandleOwnership.DoNotTransfer);
        using (var _values = new JavaArray<int>(values, JniHandleOwnership.DoNotTransfer)) {
            __this.OnAdd (_values, currentIndex, currentSum);
        }
    }
#pragma warning restore 0169
#endregion
}

JNI Nesne Başvuruları

Birçok JNIEnv yöntemi, s'ye GCHandlebenzer JNInesne başvuruları döndürür. JNI üç farklı tür nesne başvurusu sağlar: yerel başvurular, genel başvurular ve zayıf genel başvurular. Üçü de olarak System.IntPtrtemsil edilir, ancak (JNI İşlev Türleri bölümüne göre) yöntemlerden JNIEnv döndürülenlerin tümü IntPtrbaşvuru değildir. Örneğin, JNIEnv.GetMethodID bir IntPtrdöndürür, ancak nesne başvurusu döndürmez, döndürür jmethodID. Ayrıntılar için JNI işlevi belgelerine bakın.

Yerel başvurular çoğu başvuru oluşturma yöntemi tarafından oluşturulur. Android, herhangi bir zamanda sınırlı sayıda yerel başvuru bulunmasına izin verir( genellikle 512). Yerel başvurular JNIEnv.DeleteLocalRef aracılığıyla silinebilir. JNI'nin aksine, nesne başvurularını döndüren tüm başvuru JNIEnv yöntemleri yerel başvuruları döndürmez; JNIEnv.FindClass genel başvuru döndürür. Yerel başvuruları mümkün olduğunca hızlı bir şekilde silmeniz önerilir. Büyük olasılıkla nesnenin JniHandleOwnership.TransferLocalRefetrafında bir Java.Lang.Object oluşturup Java.Lang.Object(IntPtr tanıtıcısı, JniHandleOwnership aktarım) oluşturucusunun belirtilmesi gerekir.

Genel başvurular JNIEnv.NewGlobalRef ve JNIEnv.FindClass tarafından oluşturulur. JNIEnv.DeleteGlobalRef ile yok edilebilirler. Öykünücüler 2.000 bekleyen genel başvuru sınırına sahipken donanım cihazlarında 52.000 genel başvuru sınırı vardır.

Zayıf genel başvurular yalnızca Android v2.2 (Froyo) ve sonraki sürümlerde kullanılabilir. Zayıf genel başvurular JNIEnv.DeleteWeakGlobalRef ile silinebilir.

JNI Yerel Başvuruları ile ilgilenme

JNIEnv.GetObjectField, JNIEnv.GetStaticObjectField, JNIEnv.CallObjectMethod, JNIEnv.CallNonvirtualObjectMethod ve JNIEnv.CallStaticObjectMethod yöntemleri, Java nesnesine JNI yerel başvurusu içeren bir döndürür IntPtr veya IntPtr.Zero Java döndürdüysenull. Aynı anda bekleyen yerel başvuru sayısının sınırlı olması nedeniyle (512 girdi), başvuruların zamanında silindiğinden emin olmak tercih edilir. Yerel başvurularla ilgilenmenin üç yolu vardır: bunları açıkça silme, bunları tutmak için bir Java.Lang.Object örnek oluşturma ve bunların çevresinde yönetilen çağrılabilen sarmalayıcı oluşturmak için kullanma Java.Lang.Object.GetObject<T>() .

Yerel Başvuruları Açıkça Silme

JNIEnv.DeleteLocalRef , yerel başvuruları silmek için kullanılır. Yerel başvuru silindikten sonra, artık kullanılamaz, bu nedenle yerel başvuruyla yapılan son şeyin bu JNIEnv.DeleteLocalRef olduğundan emin olmak için dikkatli olunmalıdır.

IntPtr lref = JNIEnv.CallObjectMethod(instance, methodID);
try {
    // Do something with `lref`
}
finally {
    JNIEnv.DeleteLocalRef (lref);
}

Java.Lang.Object ile sarmalama

Java.Lang.Object , çıkış yapan JNI başvurularını sarmak için kullanılabilecek bir Java.Lang.Object(IntPtr tanıtıcısı, JniHandleOwnership aktarım) oluşturucu sağlar. JniHandleOwnership parametresi, parametrenin IntPtr nasıl ele alınması gerektiğini belirler:

  • JniHandleOwnership.DoNotTransfer – Oluşturulan Java.Lang.Object örnek parametresinden handle yeni bir genel başvuru oluşturur ve handle değiştirilmez. Arayan, gerekirse öğesini serbest getirmekle handle sorumludur.

  • JniHandleOwnership.TransferLocalRef – Oluşturulan Java.Lang.Object örnek parametresinden handle yeni bir genel başvuru oluşturur ve handle JNIEnv.DeleteLocalRef ile silinir. Çağıran serbest handle olmamalıdır ve oluşturucu yürütmeyi tamamladıktan sonra kullanmamalıdır handle .

  • JniHandleOwnership.TransferGlobalRef – Oluşturulan Java.Lang.Object örnek parametrenin handle sahipliğini devralacaktır. Arayan serbest handle olmamalıdır.

JNI yöntemi çağırma yöntemleri yerel refs döndüreceğinden normalde JniHandleOwnership.TransferLocalRef kullanılır:

IntPtr lref = JNIEnv.CallObjectMethod(instance, methodID);
var value = new Java.Lang.Object (lref, JniHandleOwnership.TransferLocalRef);

Oluşturulan genel başvuru, örnek çöp toplanana Java.Lang.Object kadar serbest olmayacaktır. Yapabiliyorsanız, örneğin yok edilmesi genel başvuruyu boşaltarak çöp toplamaları hızlandıracaktır:

IntPtr lref = JNIEnv.CallObjectMethod(instance, methodID);
using (var value = new Java.Lang.Object (lref, JniHandleOwnership.TransferLocalRef)) {
    // use value ...
}

Java.Lang.Object.GetObject<T>() kullanma

Java.Lang.Object , belirtilen türde bir yönetilen çağrılabilen sarmalayıcı oluşturmak için kullanılabilecek bir Java.Lang.Object.GetObject<T>(IntPtr tanıtıcısı, JniHandleOwnership aktarımı) yöntemi sağlar.

Türün T aşağıdaki gereksinimleri karşılaması gerekir:

  1. T bir başvuru türü olmalıdır.

  2. T arabirimini IJavaObject uygulamalıdır.

  3. Soyut bir sınıf veya arabirim değilse T , T parametre türlerine (IntPtr, JniHandleOwnership) sahip bir oluşturucu sağlamalıdır.

  4. Soyut bir sınıf veya arabirimseT, için T kullanılabilir bir çağırıcı olmalıdır. Çağıran, öğesini devralan T veya uygulayan T soyut olmayan bir türdür ve Bir Invoker soneki ile aynı ada T sahiptir. Örneğin, T arabirimi Java.Lang.IRunnable ise, türün Java.Lang.IRunnableInvoker mevcut olması ve gerekli (IntPtr, JniHandleOwnership) oluşturucuyu içermesi gerekir.

JNI yöntemi çağırma yöntemleri yerel refs döndüreceğinden normalde JniHandleOwnership.TransferLocalRef kullanılır:

IntPtr lrefString = JNIEnv.CallObjectMethod(instance, methodID);
Java.Lang.String value = Java.Lang.Object.GetObject<Java.Lang.String>( lrefString, JniHandleOwnership.TransferLocalRef);

Java Türlerini Arama

JNI'de bir alanı veya yöntemi aramak için, önce alan veya yöntem için bildirim türü aranmalıdır. Java türlerini aramada Android.Runtime.JNIEnv.FindClass(dize)) yöntemi kullanılır. Dize parametresi, basitleştirilmiş tür başvurusu veya Java türü için tam tür başvurusudur. Basitleştirilmiş ve tam tür başvuruları hakkında ayrıntılı bilgi için JNI Tür Başvuruları bölümüne bakın.

Not: Nesne örneklerini döndüren diğer JNIEnv tüm yöntemlerden farklı olarak, FindClass yerel başvuru değil genel başvuru döndürür.

Örnek Alanları

Alanlar alan kimlikleri aracılığıyla değiştirilir. Alan kimlikleri, alanın tanımlandığı sınıf, alanın adı ve alanın JNI Tür İmzası gerektiren JNIEnv.GetFieldID aracılığıyla elde edilir.

Alan kimliklerinin serbest olması gerekmez ve karşılık gelen Java türü yüklü olduğu sürece geçerlidir. (Android şu anda sınıfın kaldırılmasını desteklemiyor.)

Örnek alanlarını düzenlemeye yönelik iki yöntem kümesi vardır: biri örnek alanlarını okumak ve diğeri örnek alanları yazmak için. Tüm yöntem kümeleri, alan değerini okumak veya yazmak için bir alan kimliği gerektirir.

Örnek Alan Değerlerini Okuma

Örnek alan değerlerini okumak için kullanılan yöntem kümesi adlandırma desenini izler:

* JNIEnv.Get*Field(IntPtr instance, IntPtr fieldID);

alanın türü nerededir * :

Örnek Alan Değerleri Yazma

Örnek alanı değerleri yazmak için kullanılan yöntem kümesi adlandırma desenini izler:

JNIEnv.SetField(IntPtr instance, IntPtr fieldID, Type value);

burada Tür , alanın türüdür:

  • JNIEnv.SetField) – Yerleşik tür olmayan herhangi bir alanın değerini yazın( örneğin java.lang.Object , diziler ve arabirim türleri). Değer IntPtr bir JNI yerel başvurusu, JNI genel başvurusu, JNI zayıf genel başvurusu veya IntPtr.Zero (için null ) olabilir.

  • JNIEnv.SetField) – Örnek alanlarının bool değerini yazın.

  • JNIEnv.SetField) – Örnek alanlarının sbyte değerini yazın.

  • JNIEnv.SetField) – Örnek alanlarının char değerini yazın.

  • JNIEnv.SetField) – Örnek alanlarının short değerini yazın.

  • JNIEnv.SetField) – Örnek alanlarının int değerini yazın.

  • JNIEnv.SetField) – Örnek alanlarının long değerini yazın.

  • JNIEnv.SetField) – Örnek alanlarının float değerini yazın.

  • JNIEnv.SetField) – Örnek alanlarının double değerini yazın.

Statik Alanlar

Statik Alanlar, alan kimlikleri aracılığıyla değiştirilir. Alan kimlikleri, alanın tanımlandığı sınıf, alanın adı ve alanın JNI Tür İmzası gerektiren JNIEnv.GetStaticFieldID aracılığıyla elde edilir.

Alan kimliklerinin serbest olması gerekmez ve karşılık gelen Java türü yüklü olduğu sürece geçerlidir. (Android şu anda sınıfın kaldırılmasını desteklemiyor.)

Statik alanları düzenlemeye yönelik iki yöntem kümesi vardır: biri örnek alanlarını okumak ve diğeri örnek alanları yazmak için. Tüm yöntem kümeleri, alan değerini okumak veya yazmak için bir alan kimliği gerektirir.

Statik Alan Değerlerini Okuma

Statik alan değerlerini okumak için kullanılan yöntem kümesi adlandırma desenini izler:

* JNIEnv.GetStatic*Field(IntPtr class, IntPtr fieldID);

alanın türü nerededir * :

Statik Alan Değerleri Yazma

Statik alan değerleri yazmak için kullanılan yöntem kümesi adlandırma desenini izler:

JNIEnv.SetStaticField(IntPtr class, IntPtr fieldID, Type value);

burada Tür , alanın türüdür:

Örnek Yöntemleri

Örnek Yöntemleri, yöntem kimlikleri aracılığıyla çağrılır. Yöntem kimlikleri JNIEnv.GetMethodID aracılığıyla elde edilir. Bu, yöntemin tanımlandığı tür, yöntemin adı ve yöntemin JNI Tür İmzası gerektirir.

Yöntem kimliklerinin serbest olması gerekmez ve karşılık gelen Java türü yüklü olduğu sürece geçerlidir. (Android şu anda sınıfın kaldırılmasını desteklemiyor.)

Çağırma yöntemleri için iki yöntem kümesi vardır: biri yöntemleri sanal olarak çağırmak için, diğeri ise sanal olmayan yöntemleri çağırmak için. Her iki yöntem kümesi de yöntemi çağırmak için bir yöntem kimliği gerektirir ve sanal olmayan çağırma da hangi sınıf uygulamasının çağrılacağını belirtmenizi gerektirir.

Arabirim yöntemleri yalnızca bildirim türü içinde aranabilir; genişletilmiş/devralınan arabirimlerden gelen yöntemler aranamaz. Daha fazla ayrıntı için sonraki Bağlama Arabirimleri / Çağırıcı Uygulaması bölümüne bakın.

sınıfında veya herhangi bir temel sınıfta veya uygulanan arabirimde bildirilen herhangi bir yöntem aranabilir.

Sanal Yöntem Çağırma

Çağırma yöntemleri kümesi, adlandırma desenini neredeyse izler:

* JNIEnv.Call*Method( IntPtr instance, IntPtr methodID, params JValue[] args );

burada * yönteminin dönüş türüdür.

Sanal Olmayan Yöntem Çağırma

Yöntemleri çağırmak için sanal olmayan yöntemler kümesi adlandırma desenini izler:

* JNIEnv.CallNonvirtual*Method( IntPtr instance, IntPtr class, IntPtr methodID, params JValue[] args );

burada * yönteminin dönüş türüdür. Sanal olmayan yöntem çağırma genellikle bir sanal yöntemin temel yöntemini çağırmak için kullanılır.

Statik Yöntemler

Statik Yöntemler, yöntem kimlikleri aracılığıyla çağrılır. Yöntem kimlikleri JNIEnv.GetStaticMethodID aracılığıyla elde edilir. Bu, yöntemin tanımlandığı türü, yöntemin adını ve yöntemin JNI Tür İmzasını gerektirir.

Yöntem kimliklerinin serbest olması gerekmez ve karşılık gelen Java türü yüklü olduğu sürece geçerlidir. (Android şu anda sınıfın kaldırılmasını desteklemiyor.)

Statik Yöntem Çağırma

Çağırma yöntemleri kümesi, adlandırma desenini neredeyse izler:

* JNIEnv.CallStatic*Method( IntPtr class, IntPtr methodID, params JValue[] args );

burada * yönteminin dönüş türüdür.

JNI Tür İmzaları

JNI Tür İmzaları , yöntemler dışında JNI Tür Başvurularıdır (basitleştirilmiş tür başvuruları olmasa da). Yöntemlerle, JNI Tür İmzası açık bir parantezdir '('ve ardından birlikte birleştirilmiş tüm parametre türleri için tür başvuruları (ayırıcı virgüller veya başka bir şey olmadan) ve ardından kapanış parantezi ')'ve ardından yöntem dönüş türünün JNI türü başvurusudur.

Örneğin, Java yöntemi göz önünde bulundurulduğunda:

long f(int n, String s, int[] array);

JNI tür imzası şöyle olacaktır:

(ILjava/lang/String;[I)J

Genel olarak, JNI imzalarını belirlemek için komutunun kullanılması javap kesinlikle önerilir. Örneğin, java.lang.Thread.State.valueOf(String) yönteminin JNI Tür İmzası "(Ljava/lang/String;) şeklindedir Ljava/lang/Thread$State;", java.lang.Thread.State.values yönteminin JNI Tür İmzası ise "()[Ljava/lang/Thread$State;" şeklindedir. Sondaki noktalı virgüllere dikkat edin; bunlar JNI türü imzasının bir parçasıdır.

JNI Tür Başvuruları

JNI türü başvuruları Java türü başvurularından farklıdır. JNI gibi java.lang.String tam Java tür adlarını kullanamazsınız, bunun yerine bağlama bağlı olarak JNI çeşitlemelerini "java/lang/String" veya "Ljava/lang/String;"kullanmanız gerekir; ayrıntılar için aşağıya bakın. Dört tür JNI türü başvurusu vardır:

  • Yerleşik
  • Basitleştirilmiş
  • type
  • Dizi

Yerleşik Tür Başvuruları

Yerleşik tür başvuruları, yerleşik değer türlerine başvurmak için kullanılan tek bir karakterdir. Eşleme aşağıdaki gibidir:

  • "B" için sbyte .
  • "S" için short .
  • "I" için int .
  • "J" için long .
  • "F" için float .
  • "D" için double .
  • "C" için char .
  • "Z" için bool .
  • "V" yöntem dönüş türleri için void .

Basitleştirilmiş Tür Başvuruları

Basitleştirilmiş tür başvuruları yalnızca JNIEnv.FindClass(dize)) içinde kullanılabilir. Basitleştirilmiş bir tür başvurusu türetmenin iki yolu vardır:

  1. Tam java adından, paket adı içindeki ve tür adından önceki her '.' öğesini ile '/' ve bir tür adı içindeki her '.' öğesini ile '$' değiştirin.

  2. çıktısını 'unzip -l android.jar | grep JavaName' okuyun.

İki durumdan biri java.lang.Thread.State Java türünün basitleştirilmiş tür başvurusuyla java/lang/Thread$Stateeşlenmesine neden olur.

Tür Başvuruları

Tür başvurusu, yerleşik bir tür başvurusu veya ön ek ve sonek içeren 'L' basitleştirilmiş bir ';' tür başvurusudur. Java türü java.lang.String için basitleştirilmiş tür başvurusu, "java/lang/String"tür başvurusu ise şeklindedir "Ljava/lang/String;".

Tür başvuruları, Dizi türü başvuruları ve JNI İmzaları ile kullanılır.

Tür başvurusu almanın ek bir yolu da çıktısını 'javap -s -classpath android.jar fully.qualified.Java.Name'okumaktır. İlgili türe bağlı olarak, JNI adını belirlemek için bir oluşturucu bildirimi veya yöntem dönüş türü kullanabilirsiniz. Örneğin:

$ javap -classpath android.jar -s java.lang.Thread.State
Compiled from "Thread.java"
public final class java.lang.Thread$State extends java.lang.Enum{
public static final java.lang.Thread$State NEW;
  Signature: Ljava/lang/Thread$State;
public static final java.lang.Thread$State RUNNABLE;
  Signature: Ljava/lang/Thread$State;
public static final java.lang.Thread$State BLOCKED;
  Signature: Ljava/lang/Thread$State;
public static final java.lang.Thread$State WAITING;
  Signature: Ljava/lang/Thread$State;
public static final java.lang.Thread$State TIMED_WAITING;
  Signature: Ljava/lang/Thread$State;
public static final java.lang.Thread$State TERMINATED;
  Signature: Ljava/lang/Thread$State;
public static java.lang.Thread$State[] values();
  Signature: ()[Ljava/lang/Thread$State;
public static java.lang.Thread$State valueOf(java.lang.String);
  Signature: (Ljava/lang/String;)Ljava/lang/Thread$State;
static {};
  Signature: ()V
}

Thread.State bir Java sabit listesi türüdür, bu nedenle tür başvurusunun valueOf Ljava/lang/Thread$State; olduğunu belirlemek için yönteminin signature değerini kullanabiliriz.

Dizi Türü Başvuruları

Dizi türü başvuruları '[' bir JNI türü başvurusuna ön eklenmiştir. Diziler belirtilirken basitleştirilmiş tür başvuruları kullanılamaz.

Örneğin, int[] , "[I"int[][] , "[[I"ve java.lang.Object[] şeklindedir"[Ljava/lang/Object;".

Java Genel Türleri ve Tür Silme

Çoğu zaman, JNI aracılığıyla görüldüğü gibi Java genel türleri yoktur. Bazı "kırışıklıklar" vardır, ancak bu kırışıklıklar Java'nın genel üyelerle etkileşim kurmasındadır, JNI'nin genel üyeleri nasıl arayıp çağırdığı ile değil.

JNI aracılığıyla etkileşim kurarken genel tür veya üye ile genel olmayan bir tür veya üye arasında fark yoktur. Örneğin, java.lang.Class T genel türü aynı zamanda her ikisi de aynı basitleştirilmiş tür başvurusuna "java/lang/Class"sahip olan "ham" genel türüdürjava.lang.Class.><

Java Yerel Arabirim Desteği

Android.Runtime.JNIEnv , Jave Yerel Arabirimi (JNI) için yönetilen sarmalayıcıdır. JNI İşlevleri Java Yerel Arabirim Belirtimi içinde bildirilir, ancak yöntemler açık JNIEnv* parametreyi kaldırmak için değiştirilmiştir ve IntPtr , jclass, vbjmethodID. yerine jobjectkullanılır. Örneğin, JNI NewObject işlevini göz önünde bulundurun:

jobject NewObjectA(JNIEnv *env, jclass clazz, jmethodID methodID, jvalue *args);

Bu, JNIEnv.NewObject yöntemi olarak kullanıma sunulur:

public static IntPtr NewObject(IntPtr clazz, IntPtr jmethod, params JValue[] parms);

İki çağrı arasında çeviri yapmak oldukça basittir. C'de aşağıdakiler olur:

jobject CreateMapActivity(JNIEnv *env)
{
    jclass    Map_Class   = (*env)->FindClass(env, "mono/samples/googlemaps/MyMapActivity");
    jmethodID Map_defCtor = (*env)->GetMethodID (env, Map_Class, "<init>", "()V");
    jobject   instance    = (*env)->NewObject (env, Map_Class, Map_defCtor);

    return instance;
}

C# eşdeğeri şöyle olabilir:

IntPtr CreateMapActivity()
{
    IntPtr Map_Class   = JNIEnv.FindClass ("mono/samples/googlemaps/MyMapActivity");
    IntPtr Map_defCtor = JNIEnv.GetMethodID (Map_Class, "<init>", "()V");
    IntPtr instance    = JNIEnv.NewObject (Map_Class, Map_defCtor);

    return instance;
}

IntPtr'de tutulan bir Java Nesnesi örneğiniz olduğunda, büyük olasılıkla bununla bir şeyler yapmak istersiniz. Bunu yapmak için JNIEnv.CallVoidMethod() gibi JNIEnv yöntemlerini kullanabilirsiniz, ancak zaten bir analog C# sarmalayıcısı varsa JNI başvurusu üzerinde bir sarmalayıcı oluşturmak istersiniz. Bunu Extensions.JavaCast<T> uzantı yöntemi aracılığıyla yapabilirsiniz:

IntPtr lrefActivity = CreateMapActivity();

// imagine that Activity were instead an interface or abstract type...
Activity mapActivity = new Java.Lang.Object(lrefActivity, JniHandleOwnership.TransferLocalRef)
    .JavaCast<Activity>();

Java.Lang.Object.GetObject<T> yöntemini de kullanabilirsiniz:

IntPtr lrefActivity = CreateMapActivity();

// imagine that Activity were instead an interface or abstract type...
Activity mapActivity = Java.Lang.Object.GetObject<Activity>(lrefActivity, JniHandleOwnership.TransferLocalRef);

Ayrıca tüm JNI işlevleri, her JNI işlevinde JNIEnv* bulunan parametre kaldırılarak değiştirilmiştir.

Özet

JNI ile doğrudan ilgilenmek, ne pahasına olursa olsun kaçınılması gereken korkunç bir deneyimdir. Ne yazık ki, her zaman önlenebilir değildir; Umarım bu kılavuz, Android için Mono ile ilişkisiz Java olaylarını gördüğünüzde bazı yardımlar sağlar.