Работа с JNIWorking With JNI

Xamarin.Android позволяет написание приложений Android в C# вместо Java. Несколько сборок предоставляются с помощью Xamarin.Android, предоставляющие привязки для библиотеки Java, включая Mono.Android.dll и Mono.Android.GoogleMaps.dll. Тем не менее привязки не доступны для каждого возможных библиотеки Java и привязок, предоставляемых не может выполнить привязку, все типы Java и члены. Чтобы использовать свободные типы Java и члены, может использоваться собственный интерфейс Java (JNI). В этой статье показано, как использовать JNI для взаимодействия с Java типов и членов из приложений Xamarin.Android.Xamarin.Android permits writing Android apps within C# instead of Java. Several assemblies are provided with Xamarin.Android which provide bindings for Java libraries, including Mono.Android.dll and Mono.Android.GoogleMaps.dll. However, bindings are not provided for every possible Java library, and the bindings that are provided may not bind every Java type and member. To use unbound Java types and members, the Java Native Interface (JNI) may be used. This article illustrates how to use JNI to interact with Java types and members from Xamarin.Android applications.

ОбзорOverview

Это не всегда необходимых или можно создать управляемые вызываемой оболочки (MCW) для вызова кода Java.It is not always necessary or possible to create a Managed Callable Wrapper (MCW) to invoke Java code. Во многих случаях «встроенные» JNI вполне приемлемым и полезным для одноразовых использование несвязанного членов Java.In many cases, "inline" JNI is perfectly acceptable and useful for one-off use of unbound Java members. Часто бывает проще в использовании JNI для вызова отдельный метод в класс Java, чем Чтобы создать привязку к всей .jar.It is often simpler to use JNI to invoke a single method on a Java class than to generate an entire .jar binding.

Xamarin.Android предоставляет Mono.Android.dll сборку, которая обеспечивает привязку для Android android.jar библиотеки.Xamarin.Android provides the Mono.Android.dll assembly, which provides a binding for Android's android.jar library. Типы и члены не представлять Mono.Android.dll и отсутствуют типы в android.jar может использоваться, привязывая их вручную.Types and members not present within Mono.Android.dll and types not present within android.jar may be used by manually binding them. Чтобы привязать Java типов и членов, используйте Java Native Interface (JNI) для поиска типов, чтения и записи поля, а также для вызова методов.To bind Java types and members, you use the Java Native Interface (JNI) to lookup types, read and write fields, and invoke methods.

API JNI в Xamarin.Android аналогичен очень System.Reflection API в .NET: она делает доступными для просмотра типов и членов по имени, чтение и запись значений полей и вызывать методы и многое другое.The JNI API in Xamarin.Android is conceptually very similar to the System.Reflection API in .NET: it makes it possible for you to look up types and members by name, read and write field values, invoke methods, and more. Можно использовать JNI и Android.Runtime.RegisterAttribute настраиваемого атрибута для объявления виртуальные методы, которые могут быть привязаны к поддерживает переопределение.You can use JNI and the Android.Runtime.RegisterAttribute custom attribute to declare virtual methods that can be bound to support overriding. Таким образом, они могут быть реализованы в интерфейсы можно привязать C#.You can bind interfaces so that they can be implemented in C#.

В этом документе описаны:This document explains:

  • Как JNI ссылается на типы.How JNI refers to types.
  • Как выполнить уточняющий запрос, чтение и запись полей.How to lookup, read, and write fields.
  • Как для поиска и вызова методов.How to lookup and invoke methods.
  • Как предоставить виртуальные методы, чтобы разрешить переопределение из управляемого кода.How to expose virtual methods to allow overriding from managed code.
  • Способ предоставления интерфейсов.How to expose interfaces.

ТребованияRequirements

JNI, предоставляемые Android.Runtime.JNIEnv имен, доступен в каждой версии Xamarin.Android.JNI, as exposed through the Android.Runtime.JNIEnv namespace, is available in every version of Xamarin.Android. Чтобы привязать Java типы и интерфейсы, необходимо использовать Xamarin.Android версии 4.0 или более поздней версии.To bind Java types and interfaces, you must use Xamarin.Android 4.0 or later.

Управляемые вызываемых оболочекManaged Callable Wrappers

Объект управляемых вызываемая оболочка (MCW) является привязки для Java класса или интерфейса, который мы завершаем вся начинка JNI таким образом этот клиент C# код не должен беспокоиться о внутренних сложностей аппаратного JNI.A Managed Callable Wrapper (MCW) is a binding for a Java class or interface which wraps up the all the JNI machinery so that client C# code doesn't need to worry about the underlying complexity of JNI. Большая часть Mono.Android.dll состоит из управляемых вызываемых оболочек.Most of Mono.Android.dll consists of managed callable wrappers.

Управляемые вызываемых оболочек служат двум целям.Managed callable wrappers serve two purposes:

  1. Инкапсулируют используйте JNI, таким образом, чтобы клиентский код не нужно знать о внутренних сложностей.Encapsulate JNI use so that client code doesn't need to know about the underlying complexity.
  2. Делают возможным вложенным классом типов Java и реализовывать интерфейсы Java.Make it possible to sub-class Java types and implement Java interfaces.

Первой задачей является исключительно для удобства и инкапсуляции сложности, таким образом, чтобы пользователи имеют простой и управляемый набор классов для использования.The first purpose is purely for convenience and encapsulation of complexity so that consumers have a simple, managed set of classes to use. Это требует использования различных JNIEnv элементов как описано далее в этой статье.This requires use of the various JNIEnv members as described later in this article. Имейте в виду, что управляемые вызываемых оболочек не являются строго обязательными – «встроенный», используйте JNI, тогда вполне приемлемым его можно использовать для одноразовых использование несвязанного членов Java.Keep in mind that managed callable wrappers aren't strictly necessary – "inline" JNI use is perfectly acceptable and is useful for one-off use of unbound Java members. Реализация подкласса и интерфейс требует использования управляемых вызываемых оболочек.Sub-classing and interface implementation requires the use of managed callable wrappers.

Вызываемые программы-оболочки AndroidAndroid Callable Wrappers

Android вызываемых оболочек (ACW) являются обязательными, каждый раз, когда среда выполнения Android (ГРАФИКА) должна вызывать управляемый код; Эти оболочки необходимы, так как нет способа для регистрации классов с помощью КАРТИНОК во время выполнения.Android callable wrappers (ACW) are required whenever the Android runtime (ART) needs to invoke managed code; these wrappers are required because there is no way to register classes with ART at runtime. (В частности, DefineClass JNI функция не поддерживается средой выполнения Android.(Specifically, the DefineClass JNI function is not supported by the Android runtime. Вызываемые оболочки времени Android таким образом составляют для отсутствия поддержки регистрации типа среды выполнения.)Android callable wrappers thus make up for the lack of runtime type registration support.)

Когда или выполнить виртуальный метод, который является переопределяемые или реализуемые в управляемом коде интерфейса необходим код для Android, Xamarin.Android необходимо указать прокси-сервер Java, чтобы этот метод возвращает передается соответствующий управляемый тип.Whenever Android code needs to execute a virtual or interface method that is overridden or implemented in managed code, Xamarin.Android must provide a Java proxy so that this method gets dispatched to the appropriate managed type. Эти типы прокси-сервера Java имеют код Java, которое «одного» базового класса и в списке интерфейсов Java управляемый тип, реализация же конструкторы и объявление переопределенного базового класса и методов интерфейса.These Java proxy types are Java code that have the "same" base class and Java interface list as the managed type, implementing the same constructors and declaring any overridden base class and interface methods.

Android вызываемые оболочки создаются monodroid.exe программы во время процесс сборкии создаются для всех типов, которые (прямо или косвенно) наследовать Java.Lang.Object.Android callable wrappers are generated by the monodroid.exe program during the build process, and are generated for all types that (directly or indirectly) inherit Java.Lang.Object.

Реализация интерфейсовImplementing Interfaces

Бывают случаи, когда необходимо реализовать интерфейс Android (такие как Android.Content.IComponentCallbacks).There are times when you may need to implement an Android interface, (such as Android.Content.IComponentCallbacks).

Все Android классы и интерфейсы расширения Android.Runtime.IJavaObject интерфейс; таким образом, необходимо реализовать все типы Android IJavaObject.All Android classes and interfaces extend the Android.Runtime.IJavaObject interface; therefore, all Android types must implement IJavaObject. Xamarin.Android использует этот факт – используется IJavaObject для предоставления Android с помощью прокси-сервер Java (Android вызываемую оболочку) для указанного управляемого типа.Xamarin.Android takes advantage of this fact – it uses IJavaObject to provide Android with a Java proxy (an Android callable wrapper) for the given managed type. Так как monodroid.exe ищет только Java.Lang.Object подклассы (который должен реализовывать IJavaObject), работа с подклассами Java.Lang.Object предоставляет способ реализации интерфейсов в управляемом коде.Because monodroid.exe only looks for Java.Lang.Object subclasses (which must implement IJavaObject), subclassing Java.Lang.Object provides us with a way to implement interfaces in managed code. Пример:For example:

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

Сведения о реализацииImplementation Details

В оставшейся части в этой статье приводятся сведения о реализации может быть изменена без предварительного уведомления (и представленные здесь только в том случае, поскольку разработчики могут весьма интересно, что происходит за кулисами).The remainder of this article provides implementation details subject to change without notice (and is presented here only because developers may be curious about what's going on under the hood).

Например, даны следующие C# источника:For example, given the following C# source:

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 программы формирует следующие Android вызываемой оболочки:The mandroid.exe program will generate the following Android Callable Wrapper:

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);
}

Обратите внимание, что базовый класс сохраняется объявления собственного метода предоставляются для каждого метода, которая переопределяется в рамках управляемого кода.Notice that the base class is preserved, and native method declarations are provided for each method that is overridden within managed code.

ExportAttribute и ExportFieldAttributeExportAttribute and ExportFieldAttribute

Как правило Xamarin.Android автоматически создает код Java, который состоит из ACW; Это поколение основан на имен классов и методов, если класс является производным от класса Java и переопределяет существующих методов Java.Typically, Xamarin.Android automatically generates the Java code that comprises the ACW; this generation is based on the class and method names when a class derives from a Java class and overrides existing Java methods. Однако в некоторых сценариях создания кода не подходит, как показано ниже:However, in some scenarios, the code generation is not adequate, as outlined below:

  • Android поддерживают действия имена в XML-атрибуты макета, например android: onClick XML-атрибута.Android supports action names in layout XML attributes, for example the android:onClick XML attribute. Если он указан, увеличенную экземпляр представления пытается найти следующий метод Java.When it is specified, the inflated View instance tries to look up the Java method.

  • Java.io.Serializable интерфейс требует readObject и writeObject методы.The java.io.Serializable interface requires readObject and writeObject methods. Так как они не являются членами этого интерфейса, наши соответствующие управляемую реализацию не предоставляет эти методы в код Java.Since they are not members of this interface, our corresponding managed implementation does not expose these methods to Java code.

  • Android.os.Parcelable интерфейса ожидает, что класс реализации должен иметь статическое поле CREATOR типа Parcelable.Creator.The android.os.Parcelable interface expects that an implementation class must have a static field CREATOR of type Parcelable.Creator. Созданный код Java требуется некоторые явные поля.The generated Java code requires some explicit field. С нашей стандартные ситуации нет способа для выходного поля в коде Java из управляемого кода.With our standard scenario, there is no way to output field in Java code from managed code.

Поскольку создание кода не предоставляет решение для создания произвольных методов Java с произвольными именами, начиная с Xamarin.Android версии 4.2, ExportAttribute и ExportFieldAttribute были введены предлагают решения для указанных выше сценариев.Because code generation does not provide a solution to generate arbitrary Java methods with arbitrary names, starting with Xamarin.Android 4.2, the ExportAttribute and ExportFieldAttribute were introduced to offer a solution to the above scenarios. Оба атрибута находятся в Java.Interop пространство имен:Both attributes reside in the Java.Interop namespace:

  • ExportAttribute – Задает имя метода и его типы ожидаемого исключения (для того, чтобы предоставить явные «бросает» на языке Java).ExportAttribute – specifies a method name and its expected exception types (to give explicit "throws" in Java). При использовании метода, метод будет «Экспорт» Java метод, создающий код диспетчеризации для соответствующего вызова JNI для управляемого метода.When it is used on a method, the method will "export" a Java method that generates a dispatch code to the corresponding JNI invocation to the managed method. Это может использоваться с android:onClick и java.io.Serializable.This can be used with android:onClick and java.io.Serializable.

  • ExportFieldAttribute – Указывает имя поля.ExportFieldAttribute – specifies a field name. Он располагается на метод, который работает как инициализатор поля.It resides on a method that works as a field initializer. Это может использоваться с android.os.Parcelable.This can be used with android.os.Parcelable.

ExportAttribute пример проекта показано, как использовать эти атрибуты.The ExportAttribute sample project illustrates how to use these attributes.

Устранение неполадок ExportAttribute и ExportFieldAttributeTroubleshooting ExportAttribute and ExportFieldAttribute

  • Упаковка завершается сбоем из-за отсутствия Mono.Android.Export.dll – при использовании ExportAttribute или ExportFieldAttribute на некоторые методы в коде или зависимые библиотеки, необходимо добавить Mono.Android.Export.dll.Packaging fails due to missing Mono.Android.Export.dll – if you used ExportAttribute or ExportFieldAttribute on some methods in your code or dependent libraries, you have to add Mono.Android.Export.dll. Эта сборка является изолированной для поддержки код обратного вызова из Java.This assembly is isolated to support callback code from Java. Он отделен от Mono.Android.dll как добавляет дополнительный объем памяти для приложения.It is separate from Mono.Android.dll as it adds additional size to the application.

  • В сборке выпуска MissingMethodException происходит для методов экспорта – в окончательном построении MissingMethodException происходит для методов экспорта.In Release build, MissingMethodException occurs for Export methods – In Release build, MissingMethodException occurs for Export methods. (Эта проблема исправлена в последней версии Xamarin.Android).(This issue is fixed in the latest version of Xamarin.Android.)

ExportParameterAttributeExportParameterAttribute

ExportAttribute и ExportFieldAttribute предоставляют функциональные возможности, Java, можно использовать во время выполнения кода.ExportAttribute and ExportFieldAttribute provide functionality that Java run-time code can use. Этот код во время выполнения обращается к управляемого кода через созданные методы JNI, обусловленных этих атрибутов.This run-time code accesses managed code through the generated JNI methods driven by those attributes. В результате отсутствует существующий метод Java, привязывает управляемого метода; Таким образом следующий метод Java, созданный Сигнатура управляемого метода.As a result, there is no existing Java method that the managed method binds; hence, the Java method is generated from a managed method signature.

Тем не менее это не полностью определителя.However, this case is not fully determinant. Прежде всего это верно в некоторые расширенные сопоставления между управляемые типы и типы Java, такие как:Most notably, this is true in some advanced mappings between managed types and Java types such as:

  • InputStreamInputStream
  • OutputStreamOutputStream
  • XmlPullParserXmlPullParser
  • XmlResourceParserXmlResourceParser

Когда типы, такие как они необходимы для экспортированных методов ExportParameterAttribute должна использоваться явным образом предоставить соответствующий параметр или возвращаемое значение типа.When types such as these are needed for exported methods, the ExportParameterAttribute must be used to explicitly give the corresponding parameter or return value a type.

Атрибут annotationAnnotation Attribute

В Xamarin.Android 4.2, была преобразована IAnnotation типов реализации в атрибуты (System.Attribute), а также добавлена поддержка создания заметки в оболочках Java.In Xamarin.Android 4.2, we converted IAnnotation implementation types into attributes (System.Attribute), and added support for annotation generation in Java wrappers.

Это означает, что следующие изменения направления:This means the following directional changes:

  • Создает генератор привязки Java.Lang.DeprecatedAttribute из java.Lang.Deprecated (хотя он должен быть [Obsolete] в управляемом коде).The binding generator generates Java.Lang.DeprecatedAttribute from java.Lang.Deprecated (while it should be [Obsolete] in managed code).

  • Это не означает, что существующие Java.Lang.Deprecated класса будут упразднены.This does not mean that existing Java.Lang.Deprecated class will vanish. Эти объекты на основе Java может по-прежнему использоваться как обычные объекты Java (при наличии такого использования).These Java-based objects could be still used as usual Java objects (if such usage exists). Будет существовать Deprecated и DeprecatedAttribute классы.There will be Deprecated and DeprecatedAttribute classes.

  • Java.Lang.DeprecatedAttribute Класс обозначен как [Annotation] .The Java.Lang.DeprecatedAttribute class is marked as [Annotation] . При отсутствии настраиваемый атрибут, который наследуется от этого [Annotation] атрибут, задача msbuild создаст аннотацию Java для этого настраиваемого атрибута (@Deprecated) в Android вызываемой оболочки (ACW).When there is a custom attribute that is inherited from this [Annotation] attribute, msbuild task will generate a Java annotation for that custom attribute (@Deprecated) in the Android Callable Wrapper (ACW).

  • Заметки могут быть созданы на классы, методы и экспортировать поля (которые — это метод в управляемом коде).Annotations could be generated onto classes, methods and exported fields (which is a method in managed code).

Если для содержащего класса (с заметками сам класс или класс, который содержит элементы, с заметками) не зарегистрирован, всего исходного класса Java не создается, включая заметки.If the containing class (the annotated class itself, or the class that contains the annotated members) is not registered, the entire Java class source is not generated at all, including annotations. Для методов, можно указать ExportAttribute для которого вызывается метод явно создан и снабдить аннотациями.For methods, you can specify the ExportAttribute to get the method explicitly generated and annotated. Кроме того он не является компонентом «создать» определение класса заметки Java.Also, it is not a feature to "generate" a Java annotation class definition. Другими словами при определении пользовательского атрибута управляемых определенных заметку в виде, необходимо добавить другую библиотеку JAR-файлов, который содержит соответствующий класс заметки Java.In other words, if you define a custom managed attribute for a certain annotation, you'll have to add another .jar library that contains the corresponding Java annotation class. Добавление файла источника Java, который определяет тип заметки не является достаточным.Adding a Java source file that defines the annotation type is not sufficient. Компилятор Java не работает в так же, как apt.The Java compiler does not work in the same way as apt.

Кроме того применяются следующие ограничения:Additionally the following limitations apply:

  • Процесс преобразования не учитывает @Target Аннотация до сих в тип заметки.This conversion process does not consider @Target annotation on the annotation type so far.

  • Атрибуты на свойство не работает.Attributes onto a property does not work. Используйте атрибуты свойства метода получения или задания.Use attributes for property getter or setter instead.

Класс привязкиClass Binding

Связывание класс означает написание управляемых вызываемую оболочку для упрощения вызова базового типа Java.Binding a class means writing a managed callable wrapper to simplify invocation of the underlying Java type.

Привязка виртуальные и абстрактные методы, чтобы разрешить переопределение из C# требуется Xamarin.Android 4.0.Binding virtual and abstract methods to permit overriding from C# requires Xamarin.Android 4.0. Тем не менее любая версия Xamarin.Android можно привязать виртуальных методов, статические методы или невиртуальные методы без поддержки переопределений.However, any version of Xamarin.Android can bind non-virtual methods, static methods, or virtual methods without supporting overrides.

Обычно привязка содержит следующие элементы:A binding typically contains the following items:

Объявляющий тип дескриптораDeclaring Type Handle

Поле и метод поиска методы требуют ссылку на объект, ссылающийся на их объявляющий тип.The field and method lookup methods require an object reference referring to their declaring type. По соглашению, это хранится в class_ref поля:By convention, this is held in a class_ref field:

static IntPtr class_ref = JNIEnv.FindClass(CLASS);

См. в разделе тип ссылки JNI разделе Дополнительные сведения о CLASS токена.See the JNI Type References section for details about the CLASS token.

Привязка полейBinding Fields

Поля Java доступны в качестве C# свойства, например поле Java java.lang.System.in привязаны как C# свойство Java.Lang.JavaSystem.In.Java fields are exposed as C# properties, for example the Java field java.lang.System.in is bound as the C# property Java.Lang.JavaSystem.In. Кроме того поскольку JNI различает статические поля и поля экземпляров, различные методы использовать при реализации свойства.Furthermore, since JNI distinguishes between static fields and instance fields, different methods be used when implementing the properties.

Поле привязки состоит из трех наборов методов:Field binding involves three sets of methods:

  1. Регистрационный номер плательщика поле метод.The get field id method. Регистрационный номер плательщика поле метод отвечает за возврат дескриптор поля, получить значение поля и задать значение поля будет использовать методы.The get field id method is responsible for returning a field handle that the get field value and set field value methods will use. Получение идентификатора поля необходимо знать, объявление типа, имя поля и сигнатура типа JNI поля.Obtaining the field id requires knowing the declaring type, the name of the field, and the JNI type signature of the field.

  2. Получить значение поля методы.The get field value methods. Эти методы требуют дескриптором поля и несете ответственность за считывание Java значение поля.These methods require the field handle and are responsible for reading the field's value from Java. Метод, используемый зависит от типа поля.The method to use depends upon the field's type.

  3. Задать значение поля методы.The set field value methods. Эти методы требуют дескриптором поля и отвечают за запись значение поля в Java.These methods require the field handle and are responsible for writing the field's value within Java. Метод, используемый зависит от типа поля.The method to use depends upon the field's type.

Статические поля использовать JNIEnv.GetStaticFieldID, JNIEnv.GetStatic*Field, и JNIEnv.SetStaticField методы.Static fields use the JNIEnv.GetStaticFieldID, JNIEnv.GetStatic*Field, and JNIEnv.SetStaticField methods.

Поля экземпляров использовать JNIEnv.GetFieldID, JNIEnv.Get*Field, и JNIEnv.SetField методы.Instance fields use the JNIEnv.GetFieldID, JNIEnv.Get*Field, and JNIEnv.SetField methods.

Например, статическое свойство JavaSystem.In можно реализовать в виде:For example, the static property JavaSystem.In can be implemented as:

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);
    }
}

Примечание. Мы используем InputStreamInvoker.FromJniHandle для преобразования в ссылки JNI System.IO.Stream используется экземпляр и мы JniHandleOwnership.TransferLocalRef поскольку JNIEnv.GetStaticObjectField возвращает локальная ссылка.Note: We're using InputStreamInvoker.FromJniHandle to convert the JNI reference into a System.IO.Stream instance, and we're using JniHandleOwnership.TransferLocalRef because JNIEnv.GetStaticObjectField returns a local reference.

Многие из Android.Runtime типы имеют FromJniHandle ссылаются на методы, которые преобразует JNI в требуемый тип.Many of the Android.Runtime types have FromJniHandle methods which will convert a JNI reference into the desired type.

Метод привязкиMethod Binding

Java-методы представляются как C# методы и как C# свойства.Java methods are exposed as C# methods and as C# properties. Например, следующий метод Java java.lang.Runtime.runFinalizersOnExit метод привязан как Java.Lang.Runtime.RunFinalizersOnExit метод и java.lang.Object.getClass метод привязан как Java.Lang.Object.Class свойство.For example, the Java method java.lang.Runtime.runFinalizersOnExit method is bound as the Java.Lang.Runtime.RunFinalizersOnExit method, and the java.lang.Object.getClass method is bound as the Java.Lang.Object.Class property.

Вызов метода — это двухэтапный процесс:Method invocation is a two-step process:

  1. Регистрационный номер плательщика метод для вызываемого метода.The get method id for the method to invoke. Регистрационный номер плательщика метод метод отвечает за возврат дескриптор метода, который будет использовать методы вызова метода.The get method id method is responsible for returning a method handle that the method invocation methods will use. Получить идентификатор метода необходимо знать, объявление типа, имя метода и сигнатура типа JNI метода.Obtaining the method id requires knowing the declaring type, the name of the method, and the JNI type signature of the method.

  2. Вызовите метод.Invoke the method.

Как и в случае с полями, используемые методы для получения идентификатора метода и вызвать метод отличается от статических методов и методов экземпляра.Just as with fields, the methods to use to get the method id and invoke the method differ between static methods and instance methods.

Статические методы использовать JNIEnv.GetStaticMethodID() уточняющего запроса идентификатор метода, и использовать JNIEnv.CallStatic*Method семейство методов для вызова.Static methods use JNIEnv.GetStaticMethodID() to lookup the method id, and use the JNIEnv.CallStatic*Method family of methods for invocation.

Методы экземпляра использовать JNIEnv.GetMethodID уточняющего запроса идентификатор метода, и использовать JNIEnv.Call*Method и JNIEnv.CallNonvirtual*Method семейств методов для вызова.Instance methods use JNIEnv.GetMethodID to lookup the method id, and use the JNIEnv.Call*Method and JNIEnv.CallNonvirtual*Method families of methods for invocation.

Метод привязки является потенциально больше, чем просто вызов метода.Method binding is potentially more than just method invocation. Метод привязки также включает метод для переопределения (для методов абстрактные и неконечного) или (для методов интерфейса).Method binding also includes allowing a method to be overridden (for abstract and non-final methods) or implemented (for interface methods). Интерфейсы поддерживают наследование, разделе рассматриваются сложности, связанные с поддержкой виртуальных методов и методов интерфейса.The Supporting Inheritance, Interfaces section covers the complexities of supporting virtual methods and interface methods.

Статические методыStatic Methods

Привязка статический метод предполагает использование JNIEnv.GetStaticMethodID для получения дескриптора метода, затем с помощью соответствующего JNIEnv.CallStatic*Method метод, в зависимости от типа возвращаемого значения метода.Binding a static method involves using JNIEnv.GetStaticMethodID to obtain a method handle, then using the appropriate JNIEnv.CallStatic*Method method, depending on the method's return type. Ниже приведен пример привязки для Runtime.getRuntime метод:The following is an example of a binding for the Runtime.getRuntime method:

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);
}

Обратите внимание, что мы сохраняем дескриптора метода в статическом поле, id_getRuntime.Note that we store the method handle in a static field, id_getRuntime. Это для оптимизации производительности, так что дескриптор не нужно искать при каждом вызове.This is a performance optimization, so that the method handle doesn't need to be looked up on every invocation. Кэширование дескрипторов метод таким образом необязательно.It is not necessary to cache the method handle in this way. После получения дескриптора метода JNIEnv.CallStaticObjectMethod используется для вызова метода.Once the method handle is obtained, JNIEnv.CallStaticObjectMethod is used to invoke the method. JNIEnv.CallStaticObjectMethod Возвращает IntPtr которого содержит дескриптор возвращаемого экземпляра Java.JNIEnv.CallStaticObjectMethod returns an IntPtr which contains the handle of the returned Java instance. Java.Lang.Object.GetObject<T>(IntPtr, JniHandleOwnership) используется для преобразования в экземпляр строго типизированный объект дескриптора Java.Java.Lang.Object.GetObject<T>(IntPtr, JniHandleOwnership) is used to convert the Java handle into a strongly typed object instance.

Экземпляр невиртуальный метод привязкиNon-virtual Instance Method Binding

Привязка final метод экземпляра или метод экземпляра, который не требует переопределения, предполагает использование JNIEnv.GetMethodID для получения дескриптора метода, затем с помощью соответствующего JNIEnv.Call*Method метод, в зависимости от типа возвращаемого значения метода.Binding a final instance method, or an instance method which doesn't require overriding, involves using JNIEnv.GetMethodID to obtain a method handle, then using the appropriate JNIEnv.Call*Method method, depending on the method's return type. Ниже приведен пример привязки для Object.Class свойства:The following is an example of a binding for the Object.Class property:

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);
    }
}

Обратите внимание, что мы сохраняем дескриптора метода в статическом поле, id_getClass.Note that we store the method handle in a static field, id_getClass. Это для оптимизации производительности, так что дескриптор не нужно искать при каждом вызове.This is a performance optimization, so that the method handle doesn't need to be looked up on every invocation. Кэширование дескрипторов метод таким образом необязательно.It is not necessary to cache the method handle in this way. После получения дескриптора метода JNIEnv.CallStaticObjectMethod используется для вызова метода.Once the method handle is obtained, JNIEnv.CallStaticObjectMethod is used to invoke the method. JNIEnv.CallStaticObjectMethod Возвращает IntPtr которого содержит дескриптор возвращаемого экземпляра Java.JNIEnv.CallStaticObjectMethod returns an IntPtr which contains the handle of the returned Java instance. Java.Lang.Object.GetObject<T>(IntPtr, JniHandleOwnership) используется для преобразования в экземпляр строго типизированный объект дескриптора Java.Java.Lang.Object.GetObject<T>(IntPtr, JniHandleOwnership) is used to convert the Java handle into a strongly typed object instance.

Конструкторы привязкиBinding Constructors

Конструкторы — это методы Java с именем "<init>".Constructors are Java methods with the name "<init>". Как и в Java методы экземпляра, JNIEnv.GetMethodID используется для поиска дескриптора конструктора.Just as with Java instance methods, JNIEnv.GetMethodID is used to lookup the constructor handle. В отличие от методов Java JNIEnv.NewObject методы используются для вызова конструктора дескриптор метода.Unlike Java methods, the JNIEnv.NewObject methods are used to invoke the constructor method handle. Возвращаемое значение JNIEnv.NewObject является локальной ссылки JNI:The return value of JNIEnv.NewObject is a JNI local reference:

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…

Обычно класс привязки будет подкласс Java.Lang.Object.Normally a class binding will subclass Java.Lang.Object. Для подклассов Java.Lang.Object, дополнительные семантической вступает в действие: Java.Lang.Object экземпляр хранит глобальные ссылки на экземпляр Java через Java.Lang.Object.Handle свойство.When subclassing Java.Lang.Object, an additional semantic comes into play: a Java.Lang.Object instance maintains a global reference to a Java instance through the Java.Lang.Object.Handle property.

  1. Java.Lang.Object Конструктор по умолчанию будет выделять экземпляр Java.The Java.Lang.Object default constructor will allocate a Java instance.

  2. Если тип имеет RegisterAttribute , и RegisterAttribute.DoNotGenerateAcwtrue , затем экземпляр RegisterAttribute.Name типа создается с помощью его конструктора по умолчанию.If the type has a RegisterAttribute , and RegisterAttribute.DoNotGenerateAcw is true , then an instance of the RegisterAttribute.Name type is created through its default constructor.

  3. В противном случае Android вызываемая оболочка (ACW) соответствующий this.GetType создается через конструктор по умолчанию.Otherwise, the Android Callable Wrapper (ACW) corresponding to this.GetType is instantiated through its default constructor. Android вызываемые оболочки создаются во время создания пакета для каждого Java.Lang.Object подкласс, для которого RegisterAttribute.DoNotGenerateAcw не присвоено значение true.Android Callable Wrappers are generated during package creation for every Java.Lang.Object subclass for which RegisterAttribute.DoNotGenerateAcw is not set to true.

Для типов, которые являются не класса привязки, это ожидаемый семантической: создание экземпляра Mono.Samples.HelloWorld.HelloAndroid C# Java необходимо так сконструировать экземпляр mono.samples.helloworld.HelloAndroid экземпляра созданного Android вызываемую оболочку.For types which are not class bindings, this is the expected semantic: instantiating a Mono.Samples.HelloWorld.HelloAndroid C# instance should construct a Java mono.samples.helloworld.HelloAndroid instance which is a generated Android Callable Wrapper.

Для класса привязки это может быть правильное поведение, если типом Java содержит конструктор по умолчанию и/или нет других конструктора необходимо вызвать.For class bindings, this may be the correct behavior if the Java type contains a default constructor and/or no other constructor needs to be invoked. В противном случае конструктор должен быть указан которого выполняет следующие действия:Otherwise, a constructor must be provided which performs the following actions:

  1. Вызов Java.Lang.Object (IntPtr, JniHandleOwnership) вместо значения по умолчанию Java.Lang.Object конструктор.Invoking the Java.Lang.Object(IntPtr, JniHandleOwnership) instead of the default Java.Lang.Object constructor. Это необходимо, чтобы избежать создания экземпляра Java.This is needed to avoid creating a new Java instance.

  2. Проверьте значение параметра Java.Lang.Object.Handle перед созданием экземпляра любой Java.Check the value of Java.Lang.Object.Handle before creating any Java instances. Object.Handle Свойство будет иметь значение, отличное от IntPtr.Zero Android обертки было создано в коде Java и класс привязки должен содержать созданный экземпляр вызываемой оболочки времени Android.The Object.Handle property will have a value other than IntPtr.Zero if an Android Callable Wrapper was constructed in Java code, and the class binding is being constructed to contain the created Android Callable Wrapper instance. Например, когда Android создает mono.samples.helloworld.HelloAndroid , Android вызываемая оболочка будет создан экземпляр первая и Java HelloAndroid конструктор будет создан экземпляр соответствующего Mono.Samples.HelloWorld.HelloAndroid тип, с помощью Object.Handle , свойство Задайте экземпляру Java до выполнения конструктора.For example, when Android creates a mono.samples.helloworld.HelloAndroid instance, the Android Callable Wrapper will be created first , and the Java HelloAndroid constructor will create an instance of the corresponding Mono.Samples.HelloWorld.HelloAndroid type, with the Object.Handle property being set to the Java instance prior to constructor execution.

  3. Если текущий тип среды выполнения не так же, как объявление типа, а затем экземпляр соответствующей вызываемой оболочки Android необходимо создать и использовать Object.SetHandle для хранения дескриптор, возвращенный JNIEnv.CreateInstance.If the current runtime type is not the same as the declaring type, then an instance of the corresponding Android Callable Wrapper must be created, and use Object.SetHandle to store the handle returned by JNIEnv.CreateInstance.

  4. Если текущий тип среды выполнения является таким же, как объявляющий тип, затем вызвать конструктор Java и используйте Object.SetHandle для хранения дескриптор, возвращенный JNIEnv.NewInstance .If the current runtime type is the same as the declaring type, then invoke the Java constructor and use Object.SetHandle to store the handle returned by JNIEnv.NewInstance .

Например, рассмотрим java.lang.Integer(int) конструктор.For example, consider the java.lang.Integer(int) constructor. Это привязывается как:This is bound as:

// 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 методы являются вспомогательные методы для выполнения JNIEnv.FindClass, JNIEnv.GetMethodID, JNIEnv.NewObject, и JNIEnv.DeleteGlobalReference на значение, возвращаемое из JNIEnv.FindClass.The JNIEnv.CreateInstance methods are helpers to perform a JNIEnv.FindClass, JNIEnv.GetMethodID, JNIEnv.NewObject, and JNIEnv.DeleteGlobalReference on the value returned from JNIEnv.FindClass. См. подробные сведения в следующем подразделе.See the next section for details.

Поддерживает наследование, интерфейсыSupporting Inheritance, Interfaces

Создание подкласса типа Java или реализации интерфейса Java требует создания Android вызываемых оболочек (ACWs), которые создаются для каждого Java.Lang.Object подкласс процессе упаковки.Subclassing a Java type or implementing a Java interface requires the generation of Android Callable Wrappers (ACWs) that are generated for every Java.Lang.Object subclass during the packaging process. Созданием ACW осуществляется через Android.Runtime.RegisterAttribute настраиваемого атрибута.ACW generation is controlled through the Android.Runtime.RegisterAttribute custom attribute.

Для C# типов, [Register] конструктор настраиваемого атрибута требуется один аргумент: JNI упрощенное ссылка на тип Java соответствующего типа.For C# types, the [Register] custom attribute constructor requires one argument: the JNI simplified type reference for the corresponding Java type. Это позволяет, указав разные имена между Java и C#.This allows providing different names between Java and C#.

До Xamarin.Android 4.0 [Register] настраиваемого атрибута была недоступна в «alias» существующие типы Java.Prior to Xamarin.Android 4.0, the [Register] custom attribute was unavailable to "alias" existing Java types. Это обусловлено тем, в процессе создания ACW вызовет ACWs для каждого Java.Lang.Object обнаружил подкласс.This is because the ACW generation process would generate ACWs for every Java.Lang.Object subclass encountered.

Xamarin.Android 4.0 были введены RegisterAttribute.DoNotGenerateAcw свойство.Xamarin.Android 4.0 introduced the RegisterAttribute.DoNotGenerateAcw property. Это свойство указывает, что процесс создания ACW пропустить тип с заметками, позволяя объявление новых управляемых вызываемых оболочек, не приведет к ACWs, создаваемый во время создания пакета.This property instructs the ACW generation process to skip the annotated type, allowing the declaration of new Managed Callable Wrappers that will not result in ACWs being generated at package creation time. Благодаря этому существующие типы Java привязки.This allows binding existing Java types. К примеру, рассмотрим следующий простой класс Java, Adder, который содержит один метод add, который добавляет в целые числа и возвращает результат:For instance, consider the following simple Java class, Adder, which contains one method, add, that adds to integers and returns the result:

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

Adder Тип может быть связан как:The Adder type could be bound as:

[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 {
}

Здесь Adder C# тип псевдонимы Adder типа Java.Here, the Adder C# type aliases the Adder Java type. [Register] Атрибут используется для указания имени JNI mono.android.test.Adder типом Java и DoNotGenerateAcw свойство используется для предотвращения создания ACW.The [Register] attribute is used to specify the JNI name of the mono.android.test.Adder Java type, and the DoNotGenerateAcw property is used to inhibit ACW generation. Это приведет к созданию ACW для ManagedAdder типа — правильно подклассы mono.android.test.Adder типа.This will result in the generation of an ACW for the ManagedAdder type, which properly subclasses the mono.android.test.Adder type. Если RegisterAttribute.DoNotGenerateAcw свойство еще не использовался, то будет создан новый процесс сборки Xamarin.Android mono.android.test.Adder типа Java.If the RegisterAttribute.DoNotGenerateAcw property hadn't been used, then the Xamarin.Android build process would have generated a new mono.android.test.Adder Java type. Это приведет к ошибкам компиляции, как mono.android.test.Adder типа может присутствовать дважды в двух отдельных файлах.This would result in compilation errors, as the mono.android.test.Adder type would be present twice, in two separate files.

Виртуальные методы привязкиBinding Virtual Methods

ManagedAdder подклассы Java Adder , однако оно не особо интересен: C# Adder типа не определяет все виртуальные методы, поэтому ManagedAdder не может переопределить ничего.ManagedAdder subclasses the Java Adder type, but it isn't particularly interesting: the C# Adder type doesn't define any virtual methods, so ManagedAdder can't override anything.

Привязка virtual методы, чтобы разрешить переопределение подклассами требует несколько моментов, которые необходимо выполнить которого делятся на две категории:Binding virtual methods to permit overriding by subclasses requires several things that need to be done which fall into the following two categories:

  1. Метод привязкиMethod Binding

  2. Метод регистрацииMethod Registration

Метод привязкиMethod Binding

Метод привязки требуется добавить два члена поддержки для C# Adder определение: ThresholdType, и ThresholdClass.A method binding requires the addition of two support members to the C# Adder definition: ThresholdType, and ThresholdClass.

ThresholdTypeThresholdType

ThresholdType Свойство возвращает текущий тип привязки:The ThresholdType property returns the current type of the binding:

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

ThresholdType используется в метод привязки определить, когда он должен выполнять виртуальное и диспетчеризации невиртуальных методов.ThresholdType is used in the Method Binding to determine when it should perform virtual vs. non-virtual method dispatch. Всегда должны возвращать System.Type экземпляр, который соответствует объявляющего типа C# типа.It should always return a System.Type instance which corresponds to the declaring C# type.

ThresholdClassThresholdClass

ThresholdClass Свойство возвращает ссылки на класс JNI для связанного типа:The ThresholdClass property returns the JNI class reference for the bound type:

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

ThresholdClass используется в привязке метод, при вызове невиртуальных методов.ThresholdClass is used in the Method Binding when invoking non-virtual methods.

Реализация привязкиBinding Implementation

Реализация метода привязки несет ответственность за время выполнения вызова метода Java.The method binding implementation is responsible for runtime invocation of the Java method. Он также содержит [Register] объявление настраиваемого атрибута, которое является частью метода регистрации и обсуждаются в разделе метод регистрации:It also contains a [Register] custom attribute declaration that is part of the method registration, and will be discussed in the Method Registration section:

[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));
    }
}

id_add Поле содержит идентификатор метода для метода Java для вызова.The id_add field contains the method ID for the Java method to invoke. id_add Значение получается из JNIEnv.GetMethodID, который требует от объявляющего класса (class_ref), имя метода Java ("add") и JNI подписи метода ("(II)I").The id_add value is obtained from JNIEnv.GetMethodID, which requires the declaring class (class_ref), the Java method name ("add"), and the JNI signature of the method ("(II)I").

После получения идентификатор метода GetType сравнивается с ThresholdType для определения необходимости виртуальной или невиртуальный диспетчеризации.Once the method ID is obtained, GetType is compared to ThresholdType to determine if virtual or non-virtual dispatch is required. Виртуальной диспетчеризации является обязательным, если GetType соответствует ThresholdType, как Handle может ссылаться на подкласс выделенный Java, который переопределяет метод.Virtual dispatch is required when GetType matches ThresholdType, as Handle may refer to a Java-allocated subclass which overrides the method.

Когда GetType не соответствует ThresholdType, Adder подклассом (например, ManagedAdder) и Adder.Add реализации будет вызываться, только если вызван подкласс base.Add.When GetType doesn't match ThresholdType, Adder has been subclassed (e.g. by ManagedAdder), and the Adder.Add implementation will only be invoked if the subclass invoked base.Add. Это происходит невиртуальный диспетчеризации, что выгодно ThresholdClass пригодиться.This is the non-virtual dispatch case, which is where ThresholdClass comes in. ThresholdClass Указывает, какой класс Java будет предоставить реализацию метода для вызова.ThresholdClass specifies which Java class will provide the implementation of the method to invoke.

Метод регистрацииMethod Registration

Предположим, что имеется обновленный ManagedAdder определение, которое переопределяет Adder.Add метод:Assume we have an updated ManagedAdder definition which overrides the Adder.Add method:

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

Помните, что Adder.Add было [Register] настраиваемого атрибута:Recall that Adder.Add had a [Register] custom attribute:

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

[Register] Настраиваемого атрибута конструктор принимает три значения:The [Register] custom attribute constructor accepts three values:

  1. Имя метода Java, "add" в данном случае.The name of the Java method, "add" in this case.

  2. Сигнатура типа JNI метода, "(II)I" в данном случае.The JNI Type Signature of the method, "(II)I" in this case.

  3. Метод соединитель , GetAddHandler в данном случае.The connector method , GetAddHandler in this case. Методы соединитель будет рассматриваться позднее.Connector methods will be discussed later.

Первые два параметра позволяют в процессе создания ACW создать объявление метода для переопределения этого метода.The first two parameters allow the ACW generation process to generate a method declaration to override the method. Итоговый ACW должен содержать следующий код:The resulting ACW would contain some of the following code:

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);
    // ...
}

Обратите внимание, что @Override объявлен метод, который делегирует n_-префиксом метод с тем же именем.Note that an @Override method is declared, which delegates to an n_-prefixed method of the same name. Это убедитесь, что при вызове кода Java ManagedAdder.add, ManagedAdder.n_add будет вызываться, что позволит переопределение C# ManagedAdder.Add выполняемый метод.This ensure that when Java code invokes ManagedAdder.add, ManagedAdder.n_add will be invoked, which will allow the overriding C# ManagedAdder.Add method to be executed.

Таким образом, наиболее важный вопрос: как является ManagedAdder.n_add подключается к ManagedAdder.Add?Thus, the most important question: how is ManagedAdder.n_add hooked up to ManagedAdder.Add?

Java native методы регистрируются со средой выполнения Java (Android среда выполнения) через JNI RegisterNatives функция.Java native methods are registered with the Java (the Android runtime) runtime through the JNI RegisterNatives function. RegisterNatives принимает массив структур, содержащий имя метода Java JNI сигнатуре типа и указатель на функцию для вызова, следующий за JNI, соглашение о вызовах.RegisterNatives takes an array of structures containing the Java method name, the JNI Type Signature, and a function pointer to invoke that follows JNI calling convention. Указатель функции должен быть функции, принимающей два аргумента указателя, следуют параметры метода.The function pointer must be a function that takes two pointer arguments followed by the method parameters. Java ManagedAdder.n_add метод должен быть реализован через функцию, которая имеет следующий прототип C:The Java ManagedAdder.n_add method must be implemented through a function that has the following C prototype:

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

Xamarin.Android не предоставляет RegisterNatives метод.Xamarin.Android does not expose a RegisterNatives method. Вместо этого ACW и MCW вместе предоставляют сведения, необходимые для вызова RegisterNatives: ACW содержит имя метода и сигнатура типа JNI, хватает только указатель на функцию для подключения.Instead, the ACW and the MCW together provide the information necessary to invoke RegisterNatives: the ACW contains the method name and the JNI type signature, the only thing missing is a function pointer to hook up.

Именно здесь метод соединитель пригодиться.This is where the connector method comes in. Третий [Register] параметр настраиваемого атрибута является имя метода, определенного в зарегистрированного типа или базового класса зарегистрированного типа, который не принимает никаких параметров и возвращает System.Delegate.The third [Register] custom attribute parameter is the name of a method defined in the registered type or a base class of the registered type that accepts no parameters and returns a System.Delegate. Возвращенный System.Delegate в свою очередь ссылается на метод с корректной сигнатурой функция JNI.The returned System.Delegate in turn refers to a method that has the correct JNI function signature. Наконец, делегат, который возвращает метод соединитель необходимо иметь корень таким образом, чтобы сборщик Мусора не собирает его, как Java, указанный делегат.Finally, the delegate that the connector method returns must be rooted so that the GC doesn't collect it, as the delegate is being provided to Java.

#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

GetAddHandler Метод создает Func<IntPtr, IntPtr, int, int, int> делегат, который ссылается на n_Add затем вызывает метод, JNINativeWrapper.CreateDelegate.The GetAddHandler method creates a Func<IntPtr, IntPtr, int, int, int> delegate which refers to the n_Add method, then invokes JNINativeWrapper.CreateDelegate. JNINativeWrapper.CreateDelegate заключает в оболочку указанный метод, чтобы все необработанные исключения обрабатываются и приведет к вызов в блок try/catch AndroidEvent.UnhandledExceptionRaiser событий.JNINativeWrapper.CreateDelegate wraps the provided method in a try/catch block, so that any unhandled exceptions are handled and will result in raising the AndroidEvent.UnhandledExceptionRaiser event. Результирующий делегат хранится в статический cb_add переменной, таким образом, чтобы сборщик Мусора не освобождает делегат.The resulting delegate is stored in the static cb_add variable so that the GC will not free the delegate.

Наконец n_Add метод отвечает за маршалинг параметров JNI для соответствующих управляемых типов, а затем вызовите метод делегирование.Finally, the n_Add method is responsible for marshaling the JNI parameters to the corresponding managed types, then delegating the method call.

Примечание. Всегда используйте JniHandleOwnership.DoNotTransfer при получении MCW поверх экземпляра Java.Note: Always use JniHandleOwnership.DoNotTransfer when obtaining an MCW over a Java instance. Рассматривая их как ссылку на локальном (и тем самым вызывая JNIEnv.DeleteLocalRef) приведет к разрыву управляемый -> Java -> управляемого стека переходов.Treating them as a local reference (and thus calling JNIEnv.DeleteLocalRef) will break managed -> Java -> managed stack transitions.

Полная привязка AdderComplete Adder Binding

Полнофункциональное управляемое привязку для mono.android.tests.Adder тип является:The complete managed binding for the mono.android.tests.Adder type is:

[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
}

ОграниченияRestrictions

При написании тип, соответствующий следующим критериям:When writing a type that matches the following criteria:

  1. Подклассы Java.Lang.ObjectSubclasses Java.Lang.Object

  2. Имеет [Register] настраиваемого атрибутаHas a [Register] custom attribute

  3. RegisterAttribute.DoNotGenerateAcw равно trueRegisterAttribute.DoNotGenerateAcw is true

Затем для сборки Мусора взаимодействия тип не должны содержат полей, которые могут ссылаться на Java.Lang.Object или Java.Lang.Object подкласс во время выполнения.Then for GC interaction the type must not have any fields which may refer to a Java.Lang.Object or Java.Lang.Object subclass at runtime. Например, поля типа System.Object и любой другой тип интерфейса не разрешены.For example, fields of type System.Object and any interface type are not permitted. Типы, которые не могут ссылаться на Java.Lang.Object экземпляров разрешены, такие как System.String и List<int>.Types which cannot refer to Java.Lang.Object instances are permitted, such as System.String and List<int>. Это ограничение необходимо для предотвращения коллекции преждевременное объектов сборщик мусора.This restriction is to prevent premature object collection by the GC.

Если тип должен содержать поле экземпляра, которое может ссылаться на Java.Lang.Object экземпляра, то тип поля должен быть System.WeakReference или GCHandle.If the type must contain an instance field that can refer to a Java.Lang.Object instance, then the field type must be System.WeakReference or GCHandle.

Привязка абстрактные методыBinding Abstract Methods

Привязка abstract методы мало чем отличается от привязки виртуальные методы.Binding abstract methods is largely identical to binding virtual methods. Существует два отличия:There are only two differences:

  1. Абстрактный метод является абстрактным.The abstract method is abstract. Оно по-прежнему сохраняет [Register] атрибут и связанный метод регистрации, метод привязки просто перемещается Invoker типа.It still retains the [Register] attribute and the associated Method Registration, the Method Binding is just moved to the Invoker type.

  2. Отличный от abstract Invoker тип создается, который подклассы абстрактного типа.A non- abstract Invoker type is created which subclasses the abstract type. Invoker Тип должен переопределять все абстрактные методы, объявленные в базовом классе и переопределенная реализация является реализацией метода привязки, то, что можно игнорировать регистр невиртуальный диспетчеризации.The Invoker type must override all abstract methods declared in the base class, and the overridden implementation is the Method Binding implementation, though the non-virtual dispatch case can be ignored.

Например, предположим, что выше mono.android.test.Adder.add бы метод abstract.For example, assume that the above mono.android.test.Adder.add method were abstract. C# Изменить привязки, чтобы Adder.Add были абстрактные и новый AdderInvoker тип будет определяться, которые реализованы Adder.Add:The C# binding would change so that Adder.Add were abstract, and a new AdderInvoker type would be defined which implemented Adder.Add:

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));
    }
}

Invoker Тип необходимо только в том случае, при получении ссылки JNI на экземпляры, созданные Java.The Invoker type is only necessary when obtaining JNI references to Java-created instances.

Привязка интерфейсовBinding Interfaces

Привязка интерфейсов принципиально подобен классу привязки классы, содержащие виртуальные методы, но многие особенности имеют отличия заметно (и не столь малозаметные).Binding interfaces is conceptually similar to binding classes containing virtual methods, but many of the specifics differ in subtle (and not so subtle) ways. Рассмотрим следующий объявление интерфейса Java:Consider the following Java interface declaration:

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

Интерфейс привязки состоят из двух частей: C# определение интерфейса, а также определение средство вызова для интерфейса.Interface bindings have two parts: the C# interface definition, and an Invoker definition for the interface.

Определение интерфейсаInterface Definition

C# Определения интерфейса необходимо выполнить следующие требования:The C# interface definition must fulfill the following requirements:

  • Определение интерфейса должен иметь [Register] настраиваемого атрибута.The interface definition must have a [Register] custom attribute.

  • Определение интерфейса необходимо расширить IJavaObject interface.The interface definition must extend the IJavaObject interface. В противном случае будет препятствовать ACWs наследование интерфейса Java.Failure to do so will prevent ACWs from inheriting from the Java interface.

  • Каждый метод интерфейса должен содержать [Register] атрибут, указав имя соответствующего метода Java JNI подпись и метод соединителя.Each interface method must contain a [Register] attribute specifying the corresponding Java method name, the JNI signature, and the connector method.

  • Метод соединителя необходимо также указать тип, метод соединитель можно разместить на.The connector method must also specify the type that the connector method can be located on.

При привязке abstract и virtual методы, метод соединитель будет осуществляться в иерархии наследования типа регистрируемого.When binding abstract and virtual methods, the connector method would be searched within the inheritance hierarchy of the type being registered. Интерфейсы могут иметь методы, не содержащий тел, поэтому это не сработает, таким образом требование указанного типа, указывающее, где находится метод соединителя.Interfaces can have no methods containing bodies, so this doesn't work, thus the requirement that a type be specified indicating where the connector method is located. Тип задается в строке метод соединитель, после двоеточия ':', и должно быть имя типа с указанием сборки, типа, содержащего средство вызова.The type is specified within the connector method string, after a colon ':', and must be the assembly qualified type name of the type containing the invoker.

Объявления метода интерфейса являются перевод соответствующий метод Java с помощью совместимых типов.Interface method declarations are a translation of the corresponding Java method using compatible types. Для Java встроенные типы, совместимые типы являются соответствующего C# типов, например Java int — C# int.For Java builtin types, the compatible types are the corresponding C# types, e.g. Java int is C# int. Для ссылочных типов совместимый тип является типом, который может предоставить дескриптор JNI соответствующего типа Java.For reference types, the compatible type is a type that can provide a JNI handle of the appropriate Java type.

Члены интерфейса не вызываются непосредственно приложением Java – вызова будет осуществлялся с помощью типа вызова – чтобы разрешался некоторую часть гибкость.The interface members will not be directly invoked by Java – invocation will be mediated through the Invoker type – so some amount of flexibility is permitted.

Интерфейс хода выполнения Java может быть объявленные в C# как:The Java Progress interface can be declared in C# as:

[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);
}

Обратите внимание, что выше, мы были сопоставлены Java int[] параметр JavaArray<int>.Notice in the above that we map the Java int[] parameter to a JavaArray<int>. Это не является обязательным: нам удалось оно привязано к C# int[], или IList<int>, или что-то другое полностью.This isn't necessary: we could have bound it to a C# int[], or an IList<int>, or something else entirely. Выбирается тот тип, Invoker должна быть возможность его преобразования в Java int[] типа для вызова.Whatever type is chosen, the Invoker needs to be able to translate it into a Java int[] type for invocation.

Определение вызоваInvoker Definition

Invoker Определение типа должен наследовать Java.Lang.Object, реализуйте соответствующий интерфейс и предоставляют все способы подключения, на которые ссылается определение интерфейса.The Invoker type definition must inherit Java.Lang.Object, implement the appropriate interface, and provide all connection methods referenced in the interface definition. Имеется один Дополнительные предложения, отличается от класса привязки: class_ref поле и метод идентификаторы должны входить в экземпляр, не статические члены.There is one more suggestion that differs from a class binding: the class_ref field and method IDs should be instance members, not static members.

Причина предпочтительного использования члены экземпляра связана с JNIEnv.GetMethodID поведение в среде выполнения Android.The reason for preferring instance members has to do with JNIEnv.GetMethodID behavior in the Android runtime. (Это может быть также поведение Java; она не была протестирована.) JNIEnv.GetMethodID возвращает значение null, при поиске метода, поступающие от реализованного интерфейса и не объявленный интерфейс.(This may be Java behavior as well; it hasn't been tested.) JNIEnv.GetMethodID returns null when looking up a method that comes from an implemented interface and not the declared interface. Рассмотрите возможность java.util.SortedMap<K, V> Java-интерфейса, который реализует java.util.Map<K, V> интерфейс.Consider the java.util.SortedMap<K, V> Java interface, which implements the java.util.Map<K, V> interface. MAP предоставляет снимите метод, тем самым кажущихся разумным Invoker определение для SortedMap будет:Map provides a clear method, thus a seemingly reasonable Invoker definition for SortedMap would be:

// 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);
    }
     // ...
}

Выше завершится ошибкой, поскольку JNIEnv.GetMethodID вернет null при поиске Map.clear метод с помощью SortedMap экземпляра класса.The above will fail because JNIEnv.GetMethodID will return null when looking up the Map.clear method through the SortedMap class instance.

Существует два решения: интерфейс, который каждый метод поступает из отслеживания и иметь class_ref для каждого интерфейса, или оставить все как члены экземпляров и выполнять поиск метода на тип класса, а не тип интерфейса.There are two solutions to this: track which interface every method comes from, and have a class_ref for each interface, or keep everything as instance members and perform the method lookup on the most-derived class type, not the interface type. Последний выполняется Mono.Android.dll.The latter is done in Mono.Android.dll.

Определение Invoker имеет шесть разделов: конструктор, Dispose метод, ThresholdType и ThresholdClass члены, GetObject метод, реализация метода интерфейса и реализация метода соединителя.The Invoker definition has six sections: the constructor, the Dispose method, the ThresholdType and ThresholdClass members, the GetObject method, interface method implementation, and the connector method implementation.

КонструкторConstructor

Конструктор должен поиска вызываемого экземпляра класса среды выполнения и сохранения в экземпляре класса среды выполнения class_ref поля:The constructor needs to lookup the runtime class of the instance being invoked and store the runtime class in the instance class_ref field:

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);
    }
}

Примечание. Handle Свойство должно использоваться в теле конструктора и не handle параметра, как на Android v4.0 handle параметр может быть недопустимым, когда базовый конструктор завершится выполнение.Note: The Handle property must be used within the constructor body, and not the handle parameter, as on Android v4.0 the handle parameter may be invalid after the base constructor finishes executing.

Метод DisposeDispose Method

Dispose Метод должен освободить глобальной ссылки, выделенных в конструкторе:The Dispose method needs to free the global reference allocated in the constructor:

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 и ThresholdClassThresholdType and ThresholdClass

ThresholdType И ThresholdClass элементы идентичны в том, что содержится в классе привязки:The ThresholdType and ThresholdClass members are identical to what is found in a class binding:

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

Метод GetObjectGetObject Method

Статический GetObject метод требуется для поддержки Extensions.JavaCast<T>():A static GetObject method is required to support Extensions.JavaCast<T>():

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

Методы интерфейсаInterface Methods

Каждый метод интерфейса должен иметь реализацию, которая вызывает соответствующий метод Java через JNI:Every method of the interface needs to have an implementation, which invokes the corresponding Java method through JNI:

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));
    }
}

Методы соединителяConnector Methods

Методы соединителя и инфраструктуру поддержки отвечают за маршалинг параметров JNI соответствующий C# типов.The connector methods and supporting infrastructure are responsible for marshaling the JNI parameters to appropriate C# types. Java int[] параметр будет передана в качестве JNI jintArray, который является IntPtr в C#.The Java int[] parameter will be passed as a JNI jintArray, which is an IntPtr within C#. IntPtr Необходимо маршалировать в JavaArray<int> для поддержки вызова C# интерфейса:The IntPtr must be marshaled to a JavaArray<int> in order to support invoking the C# interface:

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);
        }
    }
}

Если int[] будет предпочтительнее, чем JavaList<int>, затем JNIEnv.GetArray() вместо него может использоваться:If int[] would be preferred over JavaList<int>, then JNIEnv.GetArray() could be used instead:

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

Обратите внимание, что JNIEnv.GetArray копирует весь массив между виртуальными машинами, поэтому для больших массивов в результате множество добавлена давления сборки Мусора.Note, however, that JNIEnv.GetArray copies the entire array between VMs, so for large arrays this could result in lots of added GC pressure.

Завершения определения вызоваComplete Invoker Definition

Завершения определения IAdderProgressInvoker:The complete IAdderProgressInvoker definition:

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
}

Ссылки на объекты JNIJNI Object References

Многие методы JNIEnv возвращают JNI ссылки на объекты, похожи на GCHandles.Many JNIEnv methods return JNI object references, which are similar to GCHandles. JNI предоставляет три различных типа ссылки на объекты: локальные ссылки, глобальные ссылки и слабые глобальной ссылки.JNI provides three different types of object references: local references, global references, and weak global references. Все три представляются в виде System.IntPtr, , но (как описано в разделе Типы функций JNI) не все IntPtrвозвращаемыми JNIEnv методы являются ссылками.All three are represented as System.IntPtr, but (as per the JNI Function Types section) not all IntPtrs returned from JNIEnv methods are references. Например JNIEnv.GetMethodID возвращает IntPtr, но он не возвращает ссылку на объект, он возвращает jmethodID.For example, JNIEnv.GetMethodID returns an IntPtr, but it doesn't return an object reference, it returns a jmethodID. Обратитесь к документации функции JNI сведения.Consult the JNI function documentation for details.

Локальные ссылки, создаваемые большинство методы создания ссылки.Local references are created by most reference-creating methods. Android разрешает только ограниченное число локальные ссылки, должно существовать в любой момент времени, обычно 512.Android only allows a limited number of local references to exist at any given time, usually 512. Локальные ссылки можно удалить с помощью JNIEnv.DeleteLocalRef.Local references can be deleted via JNIEnv.DeleteLocalRef. В отличие от JNI не все ссылаются на методы JNIEnv, которая ссылается на возвращаемый объект возвращать локальными ссылками; JNIEnv.FindClass возвращает глобального ссылки.Unlike JNI, not all reference JNIEnv methods which return object references return local references; JNIEnv.FindClass returns a global reference. Настоятельно рекомендуется удалить локальные ссылки, как можно скорее, возможно, создав Java.Lang.Object вокруг объекта и указав JniHandleOwnership.TransferLocalRef для Java.Lang.Object (IntPtr обработки, переноса JniHandleOwnership) конструктор.It is strongly recommended that you delete local references as quickly as you can, possibly by constructing a Java.Lang.Object around the object and specifying JniHandleOwnership.TransferLocalRef to the Java.Lang.Object(IntPtr handle, JniHandleOwnership transfer) constructor.

Глобальные ссылки, создаваемые JNIEnv.NewGlobalRef и JNIEnv.FindClass.Global references are created by JNIEnv.NewGlobalRef and JNIEnv.FindClass. Они могут быть удалены с JNIEnv.DeleteGlobalRef.They can be destroyed with JNIEnv.DeleteGlobalRef. Эмуляторы имеют ограничение в 2000 глобальные ссылки на медиафайл, а устройства имеют ограничение на число около 52,000 глобальной ссылки.Emulators have a limit of 2,000 outstanding global references, while hardware devices have a limit of around 52,000 global references.

Слабые ссылки на глобальные доступны только на версия Android 2.2 (Froyo) и более поздних версий.Weak global references are only available on Android v2.2 (Froyo) and later. Слабые ссылки на глобальные можно удалить с помощью JNIEnv.DeleteWeakGlobalRef.Weak global references can be deleted with JNIEnv.DeleteWeakGlobalRef.

Работа с JNI локальные ссылкиDealing With JNI Local References

JNIEnv.GetObjectField, JNIEnv.GetStaticObjectField, JNIEnv.CallObjectMethod, JNIEnv.CallNonvirtualObjectMethodи JNIEnv.CallStaticObjectMethod методы возвращают IntPtr содержащий локальной ссылки JNI на объект Java или IntPtr.Zero Если возвращено Java null.The JNIEnv.GetObjectField, JNIEnv.GetStaticObjectField, JNIEnv.CallObjectMethod, JNIEnv.CallNonvirtualObjectMethod and JNIEnv.CallStaticObjectMethod methods return an IntPtr which contains a JNI local reference to a Java object, or IntPtr.Zero if Java returned null. Из-за ограниченного числа локальные ссылки, неиспользуемый во после (512 записей), желательно убедитесь, что ссылки будут удалены в течение отведенного времени.Due to the limited number of local references that can be outstanding at once (512 entries), it is desirable to ensure that the references are deleted in a timely fashion. Существует три способа, которые могут быть обработаны локальные ссылки: явным образом их удаления, создания Java.Lang.Object экземпляра для хранения их и используя Java.Lang.Object.GetObject<T>() создание управляемых вызываемой оболочки вокруг них.There are three ways that local references can be dealt with: explicitly deleting them, creating a Java.Lang.Object instance to hold them, and using Java.Lang.Object.GetObject<T>() to create a managed callable wrapper around them.

Явное удаление локальные ссылкиExplicitly Deleting Local References

JNIEnv.DeleteLocalRef используется для удаления локальные ссылки.JNIEnv.DeleteLocalRef is used to delete local references. После удаления ссылки на местную, он больше не может использоваться, поэтому будьте внимательны, чтобы убедиться, что JNIEnv.DeleteLocalRef самая последняя операция, сделать с помощью локальной ссылки.Once the local reference has been deleted, it cannot be used anymore, so care must be taken to ensure that JNIEnv.DeleteLocalRef is the last thing done with the local reference.

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

Перенос с Java.Lang.ObjectWrapping with Java.Lang.Object

Java.Lang.Object предоставляет Java.Lang.Object (дескриптор IntPtr, передачи JniHandleOwnership) конструктор, который можно использовать для переноса существующей ссылки JNI.Java.Lang.Object provides a Java.Lang.Object(IntPtr handle, JniHandleOwnership transfer) constructor which can be used to wrap an exiting JNI reference. JniHandleOwnership параметр определяет, каким образом IntPtr параметра должны обрабатываться:The JniHandleOwnership parameter determines how the IntPtr parameter should be treated:

  • JniHandleOwnership.DoNotTransfer – созданный Java.Lang.Object экземпляр создаст новую ссылку из глобального handle параметра, и handle не изменяется.JniHandleOwnership.DoNotTransfer – The created Java.Lang.Object instance will create a new global reference from the handle parameter, and handle is unchanged. Вызывающий объект отвечает за освобождение handle и при необходимости.The caller is responsible to freeing handle , if necessary.

  • JniHandleOwnership.TransferLocalRef – созданный Java.Lang.Object экземпляр создаст новую ссылку из глобального handle параметра, и handle будут удалены вместе с JNIEnv.DeleteLocalRef .JniHandleOwnership.TransferLocalRef – The created Java.Lang.Object instance will create a new global reference from the handle parameter, and handle is deleted with JNIEnv.DeleteLocalRef . Вызывающий объект не должен освободить handle и не должны использовать handle после конструктора завершения выполнения.The caller must not free handle , and must not use handle after the constructor finishes executing.

  • JniHandleOwnership.TransferGlobalRef – созданный Java.Lang.Object экземпляра будет принять владение handle параметра.JniHandleOwnership.TransferGlobalRef – The created Java.Lang.Object instance will take over ownership of the handle parameter. Вызывающий объект не должен освободить handle .The caller must not free handle .

Поскольку методы вызова JNI метод возвращает локальный refs JniHandleOwnership.TransferLocalRef обычно используется:Since the JNI method invocation methods return local refs, JniHandleOwnership.TransferLocalRef would normally be used:

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

Созданный глобальной ссылки не будут освобождены до Java.Lang.Object экземпляр удаляется сборщиком мусора.The created global reference will not be freed until the Java.Lang.Object instance is garbage collected. Если существует возможность, освобождение экземпляра будет освободить глобальной ссылки, ускоряя мусора:If you are able to, disposing of the instance will free up the global reference, speeding up garbage collections:

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

С помощью Java.Lang.Object.GetObject<T>)Using Java.Lang.Object.GetObject<T>()

Java.Lang.Object предоставляет Java.Lang.Object.GetObject<T>(дескриптор IntPtr, передачи JniHandleOwnership) метод, который может использоваться для создания управляемых вызываемую оболочку указанного типа.Java.Lang.Object provides a Java.Lang.Object.GetObject<T>(IntPtr handle, JniHandleOwnership transfer) method that can be used to create a managed callable wrapper of the specified type.

Тип T необходимо выполнить следующие требования:The type T must fulfill the following requirements:

  1. T должен быть ссылочным типом.T must be a reference type.

  2. T необходимо реализовать IJavaObject интерфейс.T must implement the IJavaObject interface.

  3. Если T не является абстрактным классом или интерфейсом, затем T должны предоставлять конструктор с типами параметров (IntPtr, JniHandleOwnership) .If T is not an abstract class or interface, then T must provide a constructor with the parameter types (IntPtr, JniHandleOwnership) .

  4. Если T является абстрактным классом или интерфейсом, существует необходимо быть invoker для T .If T is an abstract class or an interface, there must be an invoker available for T . Средство вызова — это неабстрактный тип, который наследует T или реализует T , и имеет то же имя, что T с суффиксом Invoker.An invoker is a non-abstract type that inherits T or implements T , and has the same name as T with an Invoker suffix. Например, если T — это интерфейс Java.Lang.IRunnable , то типом Java.Lang.IRunnableInvoker должен существовать и должен содержать необходимые (IntPtr, JniHandleOwnership) конструктор.For example, if T is the interface Java.Lang.IRunnable , then the type Java.Lang.IRunnableInvoker must exist and must contain the required (IntPtr, JniHandleOwnership) constructor.

Поскольку методы вызова JNI метод возвращает локальный refs JniHandleOwnership.TransferLocalRef обычно используется:Since the JNI method invocation methods return local refs, JniHandleOwnership.TransferLocalRef would normally be used:

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

Поиск типов JavaLooking up Java Types

Для поиска поля или метода в JNI, объявляющий тип для поля или метода необходимо искать первый.To lookup a field or method in JNI, the declaring type for the field or method must be looked up first. Android.Runtime.JNIEnv.FindClass(string) метод используется для поиска типов Java.The Android.Runtime.JNIEnv.FindClass(string) method is used to lookup Java types. Параметр строки упрощенное ссылка на тип или ссылка на полный тип для типа Java.The string parameter is the simplified type reference or the full type reference for the Java type. См. в разделе тип ссылки JNI Дополнительные сведения о ссылках упрощенного и полного типа.See the JNI Type References section for details about simplified and full type references.

Примечание. В отличие от всех остальных JNIEnv метод, который возвращает экземпляры объектов, FindClass Возвращает глобальные ссылки, не является локальной ссылкой.Note: Unlike every other JNIEnv method which returns object instances, FindClass returns a global reference, not a local reference.

Поля экземпляраInstance Fields

Поля можно назвать идентификаторов полей.Fields are manipulated through field IDs. Поля извлекаются через JNIEnv.GetFieldID, который требует от класса, это поле определено в имя поля и сигнатура типа JNI поля.Field IDs are obtained via JNIEnv.GetFieldID, which requires the class that the field is defined in, the name of the field, and the JNI Type Signature of the field.

Поля идентификаторов не обязательно должны быть освобождены они действуют до тех пор, пока загружается соответствующего типа Java.Field IDs do not need to be freed, and are valid as long as the corresponding Java type is loaded. (Android не поддерживается выгрузки класса.)(Android does not currently support class unloading.)

Существует два набора методов для работы с полями экземпляров: один для чтения поля экземпляра и один для записи полей экземпляра.There are two sets of methods for manipulating instance fields: one for reading instance fields and one for writing instance fields. Все наборы из методов требуется идентификатор поля для чтения или записи значение поля.All sets of methods require a field ID to read or write the field value.

Считывание значений поля экземпляраReading Instance Field Values

Набор методов для чтения значения полей экземпляра использует шаблон именования:The set of methods for reading instance field values follows the naming pattern:

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

где * — это тип поля:where * is the type of the field:

  • JNIEnv.GetObjectField – прочитать значение любого поля экземпляра, такие как не типом builtin java.lang.Object , массивы и типы интерфейсов.JNIEnv.GetObjectField – Read the value of any instance field that isn't a builtin type, such as java.lang.Object , arrays, and interface types. Возвращаемое значение локальной ссылки JNI.The value returned is a JNI local reference.

  • JNIEnv.GetBooleanField – прочитать значение bool полей экземпляра.JNIEnv.GetBooleanField – Read the value of bool instance fields.

  • JNIEnv.GetByteField – прочитать значение sbyte полей экземпляра.JNIEnv.GetByteField – Read the value of sbyte instance fields.

  • JNIEnv.GetCharField – прочитать значение char полей экземпляра.JNIEnv.GetCharField – Read the value of char instance fields.

  • JNIEnv.GetShortField – прочитать значение short полей экземпляра.JNIEnv.GetShortField – Read the value of short instance fields.

  • JNIEnv.GetIntField – прочитать значение int полей экземпляра.JNIEnv.GetIntField – Read the value of int instance fields.

  • JNIEnv.GetLongField – прочитать значение long полей экземпляра.JNIEnv.GetLongField – Read the value of long instance fields.

  • JNIEnv.GetFloatField – прочитать значение float полей экземпляра.JNIEnv.GetFloatField – Read the value of float instance fields.

  • JNIEnv.GetDoubleField – прочитать значение double полей экземпляра.JNIEnv.GetDoubleField – Read the value of double instance fields.

Запись значений поля экземпляраWriting Instance Field Values

Набор методов для записи значения полей экземпляра использует шаблон именования:The set of methods for writing instance field values follows the naming pattern:

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

где тип тип поля:where Type is the type of the field:

  • JNIEnv.SetField – записать значение любого поля, такие как не типом builtin java.lang.Object , массивы и типы интерфейсов.JNIEnv.SetField – Write the value of any field that isn't a builtin type, such as java.lang.Object , arrays, and interface types. IntPtr Значение может быть локальной ссылки JNI, глобальной ссылки JNI, слабые глобальной ссылки JNI, или IntPtr.Zero (для null ).The IntPtr value may be a JNI local reference, JNI global reference, JNI weak global reference, or IntPtr.Zero (for null ).

  • JNIEnv.SetField – записать значение bool полей экземпляра.JNIEnv.SetField – Write the value of bool instance fields.

  • JNIEnv.SetField – записать значение sbyte полей экземпляра.JNIEnv.SetField – Write the value of sbyte instance fields.

  • JNIEnv.SetField – записать значение char полей экземпляра.JNIEnv.SetField – Write the value of char instance fields.

  • JNIEnv.SetField – записать значение short полей экземпляра.JNIEnv.SetField – Write the value of short instance fields.

  • JNIEnv.SetField – записать значение int полей экземпляра.JNIEnv.SetField – Write the value of int instance fields.

  • JNIEnv.SetField – записать значение long полей экземпляра.JNIEnv.SetField – Write the value of long instance fields.

  • JNIEnv.SetField – записать значение float полей экземпляра.JNIEnv.SetField – Write the value of float instance fields.

  • JNIEnv.SetField – записать значение double полей экземпляра.JNIEnv.SetField – Write the value of double instance fields.

Статические поляStatic Fields

Статические поля можно назвать идентификаторов полей.Static Fields are manipulated through field IDs. Поля извлекаются через JNIEnv.GetStaticFieldID, который требует от класса, это поле определено в имя поля и сигнатура типа JNI поля.Field IDs are obtained via JNIEnv.GetStaticFieldID, which requires the class that the field is defined in, the name of the field, and the JNI Type Signature of the field.

Поля идентификаторов не обязательно должны быть освобождены они действуют до тех пор, пока загружается соответствующего типа Java.Field IDs do not need to be freed, and are valid as long as the corresponding Java type is loaded. (Android не поддерживается выгрузки класса.)(Android does not currently support class unloading.)

Существует два набора методов для манипулирования статические поля: одно для чтения поля экземпляра и один для записи полей экземпляра.There are two sets of methods for manipulating static fields: one for reading instance fields and one for writing instance fields. Все наборы из методов требуется идентификатор поля для чтения или записи значение поля.All sets of methods require a field ID to read or write the field value.

Чтение значения статических полейReading Static Field Values

Набор методов для чтения значения статических полей использует шаблон именования:The set of methods for reading static field values follows the naming pattern:

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

где * — это тип поля:where * is the type of the field:

Запись значений в статическом полеWriting Static Field Values

Набор методов для записи значения статических полей использует шаблон именования:The set of methods for writing static field values follows the naming pattern:

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

где тип тип поля:where Type is the type of the field:

  • JNIEnv.SetStaticField – записать значение статического поля, не типом builtin, такие как java.lang.Object , массивы и типы интерфейсов.JNIEnv.SetStaticField – Write the value of any static field that isn't a builtin type, such as java.lang.Object , arrays, and interface types. IntPtr Значение может быть локальной ссылки JNI, глобальной ссылки JNI, слабые глобальной ссылки JNI, или IntPtr.Zero (для null ).The IntPtr value may be a JNI local reference, JNI global reference, JNI weak global reference, or IntPtr.Zero (for null ).

  • JNIEnv.SetStaticField – записать значение bool статические поля.JNIEnv.SetStaticField – Write the value of bool static fields.

  • JNIEnv.SetStaticField – записать значение sbyte статические поля.JNIEnv.SetStaticField – Write the value of sbyte static fields.

  • JNIEnv.SetStaticField – записать значение char статические поля.JNIEnv.SetStaticField – Write the value of char static fields.

  • JNIEnv.SetStaticField – записать значение short статические поля.JNIEnv.SetStaticField – Write the value of short static fields.

  • JNIEnv.SetStaticField – записать значение int статические поля.JNIEnv.SetStaticField – Write the value of int static fields.

  • JNIEnv.SetStaticField – записать значение long статические поля.JNIEnv.SetStaticField – Write the value of long static fields.

  • JNIEnv.SetStaticField – записать значение float статические поля.JNIEnv.SetStaticField – Write the value of float static fields.

  • JNIEnv.SetStaticField – записать значение double статические поля.JNIEnv.SetStaticField – Write the value of double static fields.

Методы экземпляраInstance Methods

Методы экземпляра вызываются с помощью метод идентификаторы.Instance Methods are invoked through method IDs. Метод извлекаются через JNIEnv.GetMethodID, который требует, чтобы тип, определенный в имя метода, метод и сигнатура типа JNI метода.Method IDs are obtained via JNIEnv.GetMethodID, which requires the type that the method is defined in, the name of the method, and the JNI Type Signature of the method.

Метод идентификаторы не обязательно должны быть освобождены они действуют до тех пор, пока загружается соответствующего типа Java.Method IDs do not need to be freed, and are valid as long as the corresponding Java type is loaded. (Android не поддерживается выгрузки класса.)(Android does not currently support class unloading.)

Существует два набора методов для вызова методов: один для практически вызов методов и один для вызова методов не виртуально.There are two sets of methods for invoking methods: one for invoking methods virtually, and one for invoking methods non-virtually. Оба набора методов требуется идентификатор метода для вызова метода и невиртуальный вызова также требуется указать, какую реализацию класса должны вызываться.Both sets of methods require a method ID to invoke the method, and non-virtual invocation also requires that you specify which class implementation should be invoked.

Методы интерфейса можно только искать в объявляющий тип; методы, которые берутся из расширенных/унаследованных интерфейсов невозможно найти.Interface methods can only be looked up within the declaring type; methods that come from extended/inherited interfaces cannot be looked up. См. в разделе последующих интерфейсах привязки / section реализация вызова для получения дополнительных сведений.See the later Binding Interfaces / Invoker Implementation section for more details.

Любой метод, объявленной в классе или любой базовый класс или реализованного интерфейса можно искать.Any method declared in the class or any base class or implemented interface can be looked up.

Вызов виртуального методаVirtual Method Invocation

Набор методов для вызова методов практически использует шаблон именования:The set of methods for invoking methods virtually follows the naming pattern:

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

где * является возвращаемым типом метода.where * is the return type of the method.

Вызов невиртуального методаNon-virtual Method Invocation

Набор методов для вызова методов не виртуально использует шаблон именования:The set of methods for invoking methods non-virtually follows the naming pattern:

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

где * является возвращаемым типом метода.where * is the return type of the method. Невиртуальный метод вызова обычно используется для вызова базового метода виртуального метода.Non-virtual method invocation is usually used to invoke the base method of a virtual method.

Статические методыStatic Methods

Статические методы вызываются с помощью метод идентификаторы.Static Methods are invoked through method IDs. Метод извлекаются через JNIEnv.GetStaticMethodID, который требует, чтобы тип, определенный в имя метода, метод и сигнатура типа JNI метода.Method IDs are obtained via JNIEnv.GetStaticMethodID, which requires the type that the method is defined in, the name of the method, and the JNI Type Signature of the method.

Метод идентификаторы не обязательно должны быть освобождены они действуют до тех пор, пока загружается соответствующего типа Java.Method IDs do not need to be freed, and are valid as long as the corresponding Java type is loaded. (Android не поддерживается выгрузки класса.)(Android does not currently support class unloading.)

Вызов статического методаStatic Method Invocation

Набор методов для вызова методов практически использует шаблон именования:The set of methods for invoking methods virtually follows the naming pattern:

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

где * является возвращаемым типом метода.where * is the return type of the method.

Сигнатуры типов JNIJNI Type Signatures

Сигнатуры типов JNI являются тип ссылки JNI (хотя не упрощенный тип ссылки), за исключением методов.JNI Type Signatures are JNI Type References (though not simplified type references), except for methods. С помощью методов, сигнатура типа JNI является открывающей круглой скобкой '(', а затем ссылок на типы для всех типов, соединенных вместе (с без разделительной запятых или что-нибудь еще), следуют закрывающую скобку параметра ')', следует ссылка на тип JNI тип возвращаемого значения метода.With methods, the JNI Type Signature is an open parenthesis '(', followed by the type references for all of the parameter types concatenated together (with no separating commas or anything else), followed by a closing parenthesis ')', followed by the JNI type reference of the method return type.

Например если метод Java:For example, given the Java method:

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

Сигнатура типа JNI будет следующим:The JNI type signature would be:

(ILjava/lang/String;[I)J

Как правило, это строго рекомендуется использовать javap команду, чтобы определить JNI подписи.In general, it is strongly recommended to use the javap command to determine JNI signatures. Например, сигнатура типа JNI из java.lang.Thread.State.valueOf(String) метод является "(Ljava/lang или строка); состояние $ Ljava/lang/потока;», тогда как JNI введите подпись java.lang.Thread.State.values метод является «() [состояние $ Ljava/lang/потока;».For example, the JNI Type Signature of the java.lang.Thread.State.valueOf(String) method is "(Ljava/lang/String;)Ljava/lang/Thread$State;", while the JNI Type Signature of the java.lang.Thread.State.values method is "()[Ljava/lang/Thread$State;". Остерегайтесь конечные точки с запятой; Эти являются частью сигнатура типа JNI.Watch out for the trailing semicolons; those are part of the JNI type signature.

Тип ссылки JNIJNI Type References

Тип ссылки JNI, отличаются от ссылок на типы Java.JNI type references are different from Java type references. Нельзя использовать полные имена типов Java, например java.lang.String с JNI, необходимо использовать варианты JNI "java/lang/String" или "Ljava/lang/String;", в зависимости от контекста; Дополнительные сведения приведены ниже.You cannot use fully qualified Java type names such as java.lang.String with JNI, you must instead use the JNI variations "java/lang/String" or "Ljava/lang/String;", depending on context; see below for details. Существует четыре типа тип ссылки JNI.There are four types of JNI type references:

  • Встроенныеbuilt-in
  • упрощенноеsimplified
  • typetype
  • arrayarray

Встроенный тип ссылкиBuilt-in Type References

Встроенный тип ссылки являются один символ, используемый для ссылки на встроенных типов значений.Built-in type references are a single character, used to reference built-in value types. Сопоставление выглядит следующим образом:The mapping is as follows:

  • "B" для sbyte ."B" for sbyte .
  • "S" для short ."S" for short .
  • "I" для int ."I" for int .
  • "J" для long ."J" for long .
  • "F" для float ."F" for float .
  • "D" для double ."D" for double .
  • "C" для char ."C" for char .
  • "Z" для bool ."Z" for bool .
  • "V" для void метод возвращаемые типы."V" for void method return types.

Упрощенное ссылок на типыSimplified Type References

Упрощенное ссылок на типы могут использоваться только в JNIEnv.FindClass(string).Simplified type references can only be used in JNIEnv.FindClass(string). Существует два способа для формирования упрощенный тип ссылки:There are two ways to derive a simplified type reference:

  1. В Java полное доменное имя замените каждый '.' в имя пакета и перед имя типа с помощью '/' и каждый '.' внутри имени типа с '$' .From a fully-qualified Java name, replace every '.' within the package name and before the type name with '/' , and every '.' within a type name with '$' .

  2. Считывать значения на выходе 'unzip -l android.jar | grep JavaName' .Read the output of 'unzip -l android.jar | grep JavaName' .

Одно из двух приведет к типом Java java.lang.Thread.State , сопоставляемого с ссылку на тип упрощенной java/lang/Thread$State.Either of the two will result in the Java type java.lang.Thread.State being mapped to the simplified type reference java/lang/Thread$State.

Ссылки на типыType References

Ссылка на тип является ссылкой встроенного типа или ссылка на тип упрощенной с 'L' префикс и ';' суффикс.A type reference is a built-in type reference or a simplified type reference with an 'L' prefix and a ';' suffix. Для типа Java java.lang.String, является ссылка на тип упрощенной "java/lang/String", а ссылка на тип "Ljava/lang/String;".For the Java type java.lang.String, the simplified type reference is "java/lang/String", while the type reference is "Ljava/lang/String;".

Ссылки на типы используются со ссылками на тип массива, так и с подписями JNI.Type references are used with Array type references and with JNI Signatures.

Дополнительный способ для получения ссылки типа является, считывая выходные данные 'javap -s -classpath android.jar fully.qualified.Java.Name'.An additional way to obtain a type reference is by reading the output of 'javap -s -classpath android.jar fully.qualified.Java.Name'. В зависимости от типа сложной, можно использовать объявление конструктора или метода типа, чтобы определить имя JNI.Depending on the type involved, you can use a constructor declaration or method return type to determine the JNI name. Пример:For example:

$ 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 является типом перечисления Java, чтобы мы могли использовать подпись valueOf метод для определения, что ссылка на тип $состояние Ljava/lang/потока;.Thread.State is a Java enum type, so we can use the Signature of the valueOf method to determine that the type reference is Ljava/lang/Thread$State;.

Массив ссылок на типыArray Type References

Ссылки на тип массива — это '[' в качестве префикса для типа ссылки JNI.Array type references are '[' prefixed to a JNI type reference. Упрощенное ссылок на типы нельзя использовать при указании массивов.Simplified type references cannot be used when specifying arrays.

Например int[]"[I", int[][]"[[I", и java.lang.Object[] является "[Ljava/lang/Object;".For example, int[] is "[I", int[][] is "[[I", and java.lang.Object[] is "[Ljava/lang/Object;".

Универсальные типы Java и стирания типаJava Generics and Type Erasure

Большинство времени, в JNI, универсальные шаблоны Java не существуют.Most of the time, as seen through JNI, Java generics do not exist. Существуют некоторые «Морщины», но эти Морщины находятся в взаимодействие Java с помощью универсальных шаблонов, не имеющая ищет и вызывает универсальными элементами JNI.There are some "wrinkles," but those wrinkles are in how Java interacts with generics, not with how JNI looks up and invokes generic members.

При работе через JNI, нет никакой разницы между универсального типа или члена и неуниверсального типа или члена.There is no difference between a generic type or member and a non-generic type or member when interacting through JNI. Например, универсальный тип java.lang.Class<T> также является «сырой» универсальным типом java.lang.Class, оба из которых имеют одну и ту же ссылку упрощенного типа, "java/lang/Class".For example, the generic type java.lang.Class<T> is also the "raw" generic type java.lang.Class, both of which have the same simplified type reference, "java/lang/Class".

Поддержка собственного интерфейса JavaJava Native Interface Support

Android.Runtime.JNIEnv является управляемой оболочки для Jave собственный интерфейс (JNI).Android.Runtime.JNIEnv is managed wrapper for the Jave Native Interface (JNI). Функции JNI, объявляются в спецификации собственного интерфейса Java, хотя методы были изменены для удаления явный JNIEnv* параметр и IntPtr используется вместо jobject, jclass, jmethodIDи т. д. Например, рассмотрим JNI NewObject функция:JNI Functions are declared within the Java Native Interface Specification, though the methods have been changed to remove the explicit JNIEnv* parameter and IntPtr is used instead of jobject, jclass, jmethodID, etc. For example, consider the JNI NewObject function:

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

Это указывается в виде JNIEnv.NewObject метод:This is exposed as the JNIEnv.NewObject method:

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

Преобразование между вызовами сравнительно проста.Translating between the two calls is reasonably straightforward. В C будет иметь:In C you would have:

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# Эквивалентное бы:The C# equivalent would be:

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;
}

Получив экземпляр объекта Java, в объект IntPtr, вы, вероятно, хотите что-то делать.Once you have a Java Object instance held in an IntPtr, you'll probably want to do something with it. Например, можно использовать методы JNIEnv JNIEnv.CallVoidMethod() чтобы сделать это, однако при наличии уже аналогичен C# программы-оболочки, то вы захотите создать оболочку по ссылке на JNI.You can use JNIEnv methods such as JNIEnv.CallVoidMethod() to do so, but if there is already an analogue C# wrapper then you'll want to construct a wrapper over the JNI reference. Можно сделать это с помощью Extensions.JavaCast () метод расширения:You can do so through the Extensions.JavaCast () extension method:

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 () метод:You can also use the Java.Lang.Object.GetObject () method:

IntPtr lrefActivity = CreateMapActivity();

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

Кроме того, все функции JNI были изменены, удалив JNIEnv* параметр присутствует в каждой функции JNI.Furthermore, all of the JNI functions have been modified by removing the JNIEnv* parameter present in every JNI function.

СводкаSummary

Работа с JNI напрямую — это ужасно, следует избегать любой ценой.Dealing directly with JNI is a terrible experience that should be avoided at all costs. К сожалению это не всегда вызывает; Будем надеяться, что в данном руководстве представлены помощь при достижении несвязанного вариантов Java с Mono для Android.Unfortunately, it's not always avoidable; hopefully this guide will provide some assistance when you hit the unbound Java cases with Mono for Android.