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:
- İstemci kodunun temel karmaşıklık hakkında bilgi sahibi olması gerekmeyecek şekilde JNI kullanımını kapsülleme.
- 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ı IJavaObject
gerekir.
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ı IJavaObject
gerekir) 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
vewriteObject
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.Creator
statik bir alanaCREATOR
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, vejava.io.Serializable
ileandroid:onClick
kullanılabilir.ExportFieldAttribute
– bir alan adı belirtir. Alan başlatıcı olarak çalışan bir yöntemde bulunur. Bu, ileandroid.os.Parcelable
kullanı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ştururJava.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ı olacakDeprecated
DeprecatedAttribute
.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:
Bağlı olan Java türüne yönelik bir JNI tanıtıcısı.
Her ilişkili alan için JNI alan kimlikleri ve özellikleri.
Her bağlı yöntem için JNI yöntem kimlikleri ve yöntemleri.
Alt sınıflama gerekiyorsa, tür bildiriminde RegisterAttribute.DoNotGenerateAcw olarak ayarlanmış bir RegisterAttribute özel özniteliğine sahip olmalıdır
true
.
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:
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.
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.
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*Field
ve JNIEnv.SetStaticField yöntemlerini kullanır.
Örnek alanları JNIEnv.GetFieldID, JNIEnv.Get*Field
ve 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:
Ç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.
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_getRuntime
depoladığı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_getClass
depoladığı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.Object
ek 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.
Varsayılan
Java.Lang.Object
oluşturucu bir Java örneği ayırır.Türün bir
RegisterAttribute
RegisterAttribute.DoNotGenerateAcw
ve isetrue
, türünRegisterAttribute.Name
bir örneği varsayılan oluşturucu aracılığıyla oluşturulur.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 ayarlanmayanRegisterAttribute.DoNotGenerateAcw
true
herJava.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:
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.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ışındaIntPtr.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 birmono.samples.helloworld.HelloAndroid
örnek oluşturduğunda, önce Android Çağrılabilen Sarmalayıcı oluşturulur ve JavaHelloAndroid
oluşturucu, oluşturucu yürütmeden önce özelliği Java örneğine ayarlanmış şekildeObject.Handle
ilgiliMono.Samples.HelloWorld.HelloAndroid
türün bir örneğini oluşturur.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.
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 & 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.FindClass
döndürülen değer üzerinde , JNIEnv.GetMethodID
, JNIEnv.NewObject
ve JNIEnv.DeleteGlobalReference
gerçekleştirmek JNIEnv.FindClass
iç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, Adder
tamsayılara ekleyip sonucu döndüren bir yöntem add
iç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:
Yöntem Bağlama
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.GetMethodID
elde 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 GetType
ThresholdType
sanal dağıtım gereklidir.
ile eşleşmediğinde GetType
ThresholdType
Adder
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:
Bu örnekte Java yönteminin
"add"
adı.Bu örnekte yönteminin
"(II)I"
JNI Tür İmzası.Bağlayıcı yöntemi
GetAddHandler
, 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.add
ManagedAdder.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.
RegisterNatives
Java 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 RegisterNatives
iç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.Delegate
kayı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 GetAddHandler
n_Add
başvuran bir Func<IntPtr, IntPtr, int, int, int>
temsilci oluşturur ve ardından JNINativeWrapper.CreateDelegate öğesini çağırır.
JNINativeWrapper.CreateDelegate
sağ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.DeleteLocalRef
alı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:
Alt
Java.Lang.Object
Özel bir
[Register]
özniteliği varRegisterAttribute.DoNotGenerateAcw
true
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 GCHandle
olmalı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:
Soyut yöntem soyut. Özniteliğini
[Register]
ve ilişkili Yöntem Kaydını korur, Yöntem Bağlama türüneInvoker
yeni taşınır.Soyut türü alt sınıflayan tür olmayan
abstract
Invoker
bir oluşturulur. TürünInvoker
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 abstract
varsayalım. C# bağlaması soyut olacak şekilde Adder.Add
değişir ve uygulayan Adder.Add
yeni 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 interface
gerekir. 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, Dispose
ThresholdType
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 jintArray
olarak 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 GCHandle
benzer 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.IntPtr
temsil edilir, ancak (JNI İşlev Türleri bölümüne göre) yöntemlerden JNIEnv
döndürülenlerin tümü IntPtr
başvuru değildir. Örneğin, JNIEnv.GetMethodID bir IntPtr
dö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.TransferLocalRef
etrafı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 parametresindenhandle
yeni bir genel başvuru oluşturur vehandle
değiştirilmez. Arayan, gerekirse öğesini serbest getirmeklehandle
sorumludur.JniHandleOwnership.TransferLocalRef – Oluşturulan
Java.Lang.Object
örnek parametresindenhandle
yeni bir genel başvuru oluşturur vehandle
JNIEnv.DeleteLocalRef ile silinir. Çağıran serbesthandle
olmamalıdır ve oluşturucu yürütmeyi tamamladıktan sonra kullanmamalıdırhandle
.JniHandleOwnership.TransferGlobalRef – Oluşturulan
Java.Lang.Object
örnek parametreninhandle
sahipliğini devralacaktır. Arayan serbesthandle
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:
T
bir başvuru türü olmalıdır.T
arabiriminiIJavaObject
uygulamalıdır.Soyut bir sınıf veya arabirim değilse
T
,T
parametre türlerine(IntPtr, JniHandleOwnership)
sahip bir oluşturucu sağlamalıdır.Soyut bir sınıf veya arabirimse
T
, içinT
kullanılabilir bir çağırıcı olmalıdır. Çağıran, öğesini devralanT
veya uygulayanT
soyut olmayan bir türdür ve Bir Invoker soneki ile aynı adaT
sahiptir. Örneğin, T arabirimiJava.Lang.IRunnable
ise, türünJava.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 *
:
JNIEnv.GetObjectField – , dizileri ve arabirim türleri gibi
java.lang.Object
yerleşik bir tür olmayan herhangi bir örnek alanının değerini okuyun. Döndürülen değer bir JNI yerel başvurusudur.JNIEnv.GetBooleanField – Örnek alanlarının
bool
değerini okuyun.JNIEnv.GetByteField – Örnek alanlarının
sbyte
değerini okuyun.JNIEnv.GetCharField – Örnek alanlarının
char
değerini okuyun.JNIEnv.GetShortField – Örnek alanlarının
short
değerini okuyun.JNIEnv.GetIntField – Örnek alanlarının
int
değerini okuyun.JNIEnv.GetLongField – Örnek alanlarının
long
değerini okuyun.JNIEnv.GetFloatField – Örnek alanlarının
float
değerini okuyun.JNIEnv.GetDoubleField – Örnek alanlarının
double
değerini okuyun.
Ö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ğerIntPtr
bir JNI yerel başvurusu, JNI genel başvurusu, JNI zayıf genel başvurusu veyaIntPtr.Zero
(içinnull
) 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 *
:
JNIEnv.GetStaticObjectField – Yerleşik bir tür olmayan, diziler ve arabirim türleri gibi
java.lang.Object
herhangi bir statik alanın değerini okuyun. Döndürülen değer bir JNI yerel başvurusudur.JNIEnv.GetStaticBooleanField – Statik alanların değerini
bool
okuyun.JNIEnv.GetStaticByteField – Statik alanların değerini
sbyte
okuyun.JNIEnv.GetStaticCharField – Statik alanların değerini
char
okuyun.JNIEnv.GetStaticShortField – Statik alanların değerini
short
okuyun.JNIEnv.GetStaticLongField – Statik alanların değerini
long
okuyun.JNIEnv.GetStaticFloatField – Statik alanların değerini
float
okuyun.JNIEnv.GetStaticDoubleField – Statik alanların değerini
double
okuyun.
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:
JNIEnv.SetStaticField) – Yerleşik bir tür olmayan, diziler ve arabirim türleri gibi
java.lang.Object
herhangi bir statik alanın değerini yazın. DeğerIntPtr
bir JNI yerel başvurusu, JNI genel başvurusu, JNI zayıf genel başvurusu veyaIntPtr.Zero
(içinnull
) olabilir.JNIEnv.SetStaticField) – Statik alanların değerini
bool
yazın.JNIEnv.SetStaticField) – Statik alanların değerini
sbyte
yazın.JNIEnv.SetStaticField) – Statik alanların değerini
char
yazın.JNIEnv.SetStaticField) – Statik alanların değerini
short
yazın.JNIEnv.SetStaticField) – Statik alanların değerini
int
yazın.JNIEnv.SetStaticField) – Statik alanların değerini
long
yazın.JNIEnv.SetStaticField) – Statik alanların değerini
float
yazın.JNIEnv.SetStaticField) – Statik alanların değerini
double
yazın.
Ö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.
JNIEnv.CallObjectMethod – , dizileri ve arabirimleri gibi
java.lang.Object
yerleşik olmayan bir tür döndüren bir yöntemi çağırın. Döndürülen değer bir JNI yerel başvurusudur.JNIEnv.CallBooleanMethod – Değer döndüren bir
bool
yöntem çağır.JNIEnv.CallByteMethod – Değer döndüren bir
sbyte
yöntem çağır.JNIEnv.CallCharMethod – Değer döndüren bir
char
yöntem çağır.JNIEnv.CallShortMethod – Değer döndüren bir
short
yöntem çağır.JNIEnv.CallLongMethod – Değer döndüren bir
long
yöntem çağır.JNIEnv.CallFloatMethod – Değer döndüren bir
float
yöntem çağır.JNIEnv.CallDoubleMethod – Değer döndüren bir
double
yöntem çağı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.
JNIEnv.CallNonvirtualObjectMethod – Sanal olmayan bir yöntem çağırarak , dizileri ve arabirimleri gibi
java.lang.Object
yerleşik olmayan bir tür döndürür. Döndürülen değer bir JNI yerel başvurusudur.JNIEnv.CallNonvirtualBooleanMethod – Sanal olmayan bir yöntem çağırarak bir
bool
değer döndürür.JNIEnv.CallNonvirtualByteMethod – Sanal olmayan bir yöntem çağırarak bir
sbyte
değer döndürür.JNIEnv.CallNonvirtualCharMethod – Sanal olmayan bir yöntem çağırarak bir
char
değer döndürür.JNIEnv.CallNonvirtualShortMethod – Sanal olmayan bir yöntem çağırarak bir
short
değer döndürür.JNIEnv.CallNonvirtualLongMethod – Sanal olmayan bir yöntem çağırarak bir
long
değer döndürür.JNIEnv.CallNonvirtualFloatMethod – Sanal olmayan bir yöntem çağırarak bir
float
değer döndürür.JNIEnv.CallNonvirtualDoubleMethod – Sanal olmayan bir yöntem çağırarak bir
double
değer döndürü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.
JNIEnv.CallStaticObjectMethod – , dizileri ve arabirimleri gibi
java.lang.Object
yerleşik olmayan bir tür döndüren statik bir yöntem çağırın. Döndürülen değer bir JNI yerel başvurusudur.JNIEnv.CallStaticBooleanMethod – Değer döndüren statik bir
bool
yöntem çağır.JNIEnv.CallStaticByteMethod – Bir değer döndüren statik bir
sbyte
yöntem çağır.JNIEnv.CallStaticCharMethod – Bir değer döndüren statik bir
char
yöntem çağır.JNIEnv.CallStaticShortMethod – Bir değer döndüren statik bir
short
yöntem çağır.JNIEnv.CallStaticLongMethod – Bir değer döndüren statik bir
long
yöntem çağır.JNIEnv.CallStaticFloatMethod – Değer döndüren statik bir
float
yöntem çağır.JNIEnv.CallStaticDoubleMethod – Değer döndüren statik bir
double
yöntem çağı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çinsbyte
."S"
içinshort
."I"
içinint
."J"
içinlong
."F"
içinfloat
."D"
içindouble
."C"
içinchar
."Z"
içinbool
."V"
yöntem dönüş türleri içinvoid
.
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:
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.çı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$State
eş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 jobject
kullanı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.