Metadata Pengikatan Java

Penting

Saat ini kami sedang menyelidiki penggunaan pengikatan kustom pada platform Xamarin. Silakan ikuti survei ini untuk menginformasikan upaya pengembangan di masa mendatang.

Kode C# di Xamarin.Android memanggil pustaka Java melalui pengikatan, yang merupakan mekanisme yang mengabstraksi detail tingkat rendah yang ditentukan dalam Java Native Interface (JNI). Xamarin.Android menyediakan alat yang menghasilkan pengikatan ini. Alat ini memungkinkan pengembang mengontrol bagaimana pengikatan dibuat dengan menggunakan metadata, yang memungkinkan prosedur seperti memodifikasi namespace layanan dan mengganti nama anggota. Dokumen ini membahas cara kerja metadata, meringkas atribut yang didukung metadata, dan menjelaskan cara mengatasi masalah pengikatan dengan memodifikasi metadata ini.

Gambaran Umum

Pustaka Pengikatan Java Xamarin.Android mencoba mengotomatiskan banyak pekerjaan yang diperlukan untuk mengikat pustaka Android yang ada dengan bantuan alat yang terkadang dikenal sebagai Generator Pengikatan. Saat mengikat pustaka Java, Xamarin.Android akan memeriksa kelas Java dan menghasilkan daftar semua paket, jenis, dan anggota yang akan terikat. Daftar API ini disimpan dalam file XML yang dapat ditemukan di {project directory}\obj\Release\api.xml untuk build RILIS dan di {project directory}\obj\Debug\api.xml untuk build DEBUG .

Lokasi file api.xml di folder obj/Debug

Generator Pengikatan akan menggunakan file api.xml sebagai pedoman untuk menghasilkan kelas pembungkus C# yang diperlukan. Konten file XML ini adalah variasi format Proyek Sumber Terbuka Android Google. Cuplikan berikut adalah contoh konten api.xml:

<api>
    <package name="android">
        <class abstract="false" deprecated="not deprecated" extends="java.lang.Object"
            extends-generic-aware="java.lang.Object" 
            final="true" 
            name="Manifest" 
            static="false" 
            visibility="public">
            <constructor deprecated="not deprecated" final="false"
                name="Manifest" static="false" type="android.Manifest"
                visibility="public">
            </constructor>
        </class>
...
</api>

Dalam contoh ini, api.xml mendeklarasikan kelas dalam android paket bernama Manifest yang memperluas java.lang.Object.

Dalam banyak kasus, bantuan manusia diperlukan untuk membuat Java API merasa lebih ".NET suka" atau untuk memperbaiki masalah yang mencegah perakitan pengikatan dikompilasi. Misalnya, mungkin perlu mengubah nama paket Java menjadi namespace layanan .NET, mengganti nama kelas, atau mengubah jenis pengembalian metode.

Perubahan ini tidak dicapai dengan memodifikasi api.xml secara langsung. Sebagai gantinya, perubahan direkam dalam file XML khusus yang disediakan oleh templat Pustaka Pengikatan Java. Saat mengkompilasi rakitan pengikatan Xamarin.Android, Generator Pengikatan akan dipengaruhi oleh file pemetaan ini saat membuat rakitan pengikatan

File pemetaan XML ini dapat ditemukan di folder Transformasi proyek:

  • MetaData.xml – Memungkinkan perubahan dilakukan pada API akhir, seperti mengubah namespace pengikatan yang dihasilkan.

  • EnumFields.xml – Berisi pemetaan antara konstanta Java int dan C# enums .

  • EnumMethods.xml – Memungkinkan perubahan parameter metode dan mengembalikan jenis dari konstanta Java int ke C# enums .

File MetaData.xml adalah impor paling banyak dari file-file ini karena memungkinkan perubahan tujuan umum pada pengikatan seperti:

  • Mengganti nama namespace, kelas, metode, atau bidang sehingga mengikuti konvensi .NET.

  • Menghapus namespace, kelas, metode, atau bidang yang tidak diperlukan.

  • Memindahkan kelas ke namespace yang berbeda.

  • Menambahkan kelas dukungan tambahan untuk membuat desain pengikatan mengikuti pola kerangka kerja .NET.

Mari kita lanjutkan untuk membahas Metadata.xml secara lebih rinci.

File Transformasi Metadata.xml

Seperti yang telah kita pelajari, file Metadata.xml digunakan oleh Generator Pengikatan untuk memengaruhi pembuatan rakitan pengikatan. Format metadata menggunakan sintaks XPath dan hampir identik dengan Metadata GAPI yang dijelaskan dalam panduan Metadata GAPI. Implementasi ini hampir merupakan implementasi lengkap XPath 1.0 dan dengan demikian mendukung item dalam standar 1.0. File ini adalah mekanisme berbasis XPath yang kuat untuk mengubah, menambahkan, menyembunyikan, atau memindahkan elemen atau atribut apa pun dalam file API. Semua elemen aturan dalam spesifikasi metadata menyertakan atribut jalur untuk mengidentifikasi node tempat aturan akan diterapkan. Aturan diterapkan dalam urutan berikut:

  • add-node – Menambahkan simpul anak ke simpul yang ditentukan oleh atribut jalur.
  • attr – Mengatur nilai atribut elemen yang ditentukan oleh atribut jalur.
  • remove-node – Menghapus simpul yang cocok dengan XPath tertentu.

Berikut ini adalah contoh file Metadata.xml :

<metadata>
    <!-- Normalize the namespace for .NET -->
    <attr path="/api/package[@name='com.evernote.android.job']" 
        name="managedName">Evernote.AndroidJob</attr>

    <!-- Don't  need these packages for the Xamarin binding/public API --> 
    <remove-node path="/api/package[@name='com.evernote.android.job.v14']" />
    <remove-node path="/api/package[@name='com.evernote.android.job.v21']" />

    <!-- Change a parameter name from the generic p0 to a more meaningful one. -->
    <attr path="/api/package[@name='com.evernote.android.job']/class[@name='JobManager']/method[@name='forceApi']/parameter[@name='p0']" 
        name="name">api</attr>
</metadata>

Berikut ini mencantumkan beberapa elemen XPath yang lebih umum digunakan untuk Java API:

  • interface – Digunakan untuk menemukan antarmuka Java. Misalnya, /interface[@name='AuthListener'].

  • class – Digunakan untuk menemukan kelas . Misalnya, /class[@name='MapView'].

  • method – Digunakan untuk menemukan metode pada kelas atau antarmuka Java. Misalnya, /class[@name='MapView']/method[@name='setTitleSource'].

  • parameter – Identifikasi parameter untuk metode . Mis. /parameter[@name='p0']

Menambahkan Jenis

Elemen ini add-node akan memberi tahu proyek pengikatan Xamarin.Android untuk menambahkan kelas pembungkus baru ke api.xml. Misalnya, cuplikan berikut akan mengarahkan Generator Pengikatan untuk membuat kelas dengan konstruktor dan satu bidang:

<add-node path="/api/package[@name='org.alljoyn.bus']">
    <class abstract="false" deprecated="not deprecated" final="false" name="AuthListener.AuthRequest" static="true" visibility="public" extends="java.lang.Object">
        <constructor deprecated="not deprecated" final="false" name="AuthListener.AuthRequest" static="false" type="org.alljoyn.bus.AuthListener.AuthRequest" visibility="public" />
        <field name="p0" type="org.alljoyn.bus.AuthListener.Credentials" />
    </class>
</add-node>

Menghapus Jenis

Dimungkinkan untuk menginstruksikan Generator Pengikatan Xamarin.Android untuk mengabaikan jenis Java dan tidak mengikatnya. Ini dilakukan dengan menambahkan remove-node elemen XML ke file metadata.xml :

<remove-node path="/api/package[@name='{package_name}']/class[@name='{name}']" />

Mengganti Nama Anggota

Mengganti nama anggota tidak dapat dilakukan dengan langsung mengedit file api.xml karena Xamarin.Android memerlukan nama Java Native Interface (JNI) asli. Oleh karena itu, //class/@name atribut tidak dapat diubah; jika ya, pengikatan tidak akan berfungsi.

Pertimbangkan kasus di mana kita ingin mengganti nama jenis, android.Manifest. Untuk mencapai hal ini, kami mungkin mencoba untuk langsung mengedit api.xml dan mengganti nama kelas seperti ini:

<attr path="/api/package[@name='android']/class[@name='Manifest']" 
    name="name">NewName</attr>

Ini akan mengakibatkan Pengikatan Generator membuat kode C# berikut untuk kelas pembungkus:

[Register ("android/NewName")]
public class NewName : Java.Lang.Object { ... }

Perhatikan bahwa kelas pembungkus telah diganti namanya menjadi NewName, sementara jenis Java asli masih Manifest. Tidak mungkin lagi bagi kelas pengikatan Xamarin.Android untuk mengakses metode apa pun di android.Manifest; kelas pembungkus terikat ke jenis Java yang tidak ada.

Untuk mengubah nama terkelola dengan benar dari jenis yang dibungkus (atau metode), perlu untuk mengatur atribut seperti yang managedName ditunjukkan dalam contoh ini:

<attr path="/api/package[@name='android']/class[@name='Manifest']" 
    name="managedName">NewName</attr>

Mengganti nama EventArg Kelas Pembungkus

Ketika generator pengikatan Xamarin.Android mengidentifikasi onXXX metode setter untuk jenis pendengar, peristiwa C# dan EventArgs subkelas akan dihasilkan untuk mendukung API berbumbu .NET untuk pola pendengar berbasis Java. Sebagai contoh, pertimbangkan kelas dan metode Java berikut:

com.someapp.android.mpa.guidance.NavigationManager.on2DSignNextManuever(NextManueverListener listener);

Xamarin.Android akan menghilangkan awalan on dari metode setter dan sebaliknya menggunakan 2DSignNextManuever sebagai dasar untuk nama EventArgs subkelas. Subkelas akan diberi nama sesuatu yang mirip dengan:

NavigationManager.2DSignNextManueverEventArgs

Ini bukan nama kelas C# legal. Untuk memperbaiki masalah ini, pembuat pengikatan harus menggunakan argsType atribut dan memberikan nama C# yang valid untuk EventArgs subkelas:

<attr path="/api/package[@name='com.someapp.android.mpa.guidance']/
    interface[@name='NavigationManager.Listener']/
    method[@name='on2DSignNextManeuver']" 
    name="argsType">NavigationManager.TwoDSignNextManueverEventArgs</attr>

Atribut yang Didukung

Bagian berikut menjelaskan beberapa atribut untuk mengubah API Java.

argsType

Atribut ini ditempatkan pada metode setter untuk memberi nama EventArg subkelas yang akan dihasilkan untuk mendukung pendengar Java. Ini dijelaskan secara lebih rinci di bawah ini di bagian Mengganti Nama Kelas Pembungkus EventArg nanti dalam panduan ini.

eventName

Menentukan nama untuk suatu peristiwa. Jika kosong, ini menghambat pembuatan peristiwa. Ini dijelaskan secara lebih rinci di bagian berjudul Mengganti Nama Kelas Pembungkus EventArg.

managedName

Ini digunakan untuk mengubah nama paket, kelas, metode, atau parameter. Misalnya untuk mengubah nama kelas MyClass Java menjadi NewClassName:

<attr path="/api/package[@name='com.my.application']/class[@name='MyClass']" 
    name="managedName">NewClassName</attr>

Contoh berikutnya mengilustrasikan ekspresi XPath untuk mengganti nama metode java.lang.object.toString menjadi Java.Lang.Object.NewManagedName:

<attr path="/api/package[@name='java.lang']/class[@name='Object']/method[@name='toString']" 
    name="managedName">NewMethodName</attr>

managedType

managedType digunakan untuk mengubah jenis pengembalian metode. Dalam beberapa situasi, Generator Pengikatan akan salah menyimpulkan jenis pengembalian metode Java, yang akan mengakibatkan kesalahan waktu kompilasi. Salah satu solusi yang mungkin dalam situasi ini adalah mengubah jenis pengembalian metode.

Misalnya, Generator Pengikatan percaya bahwa metode de.neom.neoreadersdk.resolution.compareTo() Java harus mengembalikan int dan mengambil Object sebagai parameter, yang menghasilkan pesan kesalahan Kesalahan CS0535: 'DE. Neom.Neoreadersdk.Resolution' tidak menerapkan anggota antarmuka 'Java.Lang.IComparable.CompareTo(Java.Lang.Object)'. Cuplikan berikut menunjukkan cara mengubah jenis parameter pertama dari metode C# yang dihasilkan dari menjadi DE.Neom.Neoreadersdk.Resolution Java.Lang.Object:

<attr path="/api/package[@name='de.neom.neoreadersdk']/
    class[@name='Resolution']/
    method[@name='compareTo' and count(parameter)=1 and
    parameter[1][@type='de.neom.neoreadersdk.Resolution']]/
    parameter[1]" name="managedType">Java.Lang.Object</attr> 

managedReturn

Mengubah jenis pengembalian metode. Ini tidak mengubah atribut pengembalian (karena perubahan pada atribut yang dikembalikan dapat mengakibatkan perubahan yang tidak kompatibel pada tanda tangan JNI). Dalam contoh berikut, jenis append pengembalian metode diubah dari SpannableStringBuilder ke IAppendable (ingat bahwa C# tidak mendukung jenis pengembalian kovarian):

<attr path="/api/package[@name='android.text']/
    class[@name='SpannableStringBuilder']/
    method[@name='append']" 
    name="managedReturn">Java.Lang.IAppendable</attr>

dikaburkan

Alat yang mengaburkan pustaka Java dapat mengganggu Generator Pengikatan Xamarin.Android dan kemampuannya untuk menghasilkan kelas pembungkus C#. Karakteristik kelas yang dikaburkan meliputi:

  • Nama kelas mencakup $, yaitu $.class
  • Nama kelas sepenuhnya disusupi dari karakter huruf kecil, yaitu a.class

Cuplikan ini adalah contoh cara menghasilkan jenis C# "tidak dikaburkan":

<attr path="/api/package[@name='{package_name}']/class[@name='{name}']" 
    name="obfuscated">false</attr>

propertyName

Atribut ini dapat digunakan untuk mengubah nama properti terkelola.

Kasus khusus penggunaan propertyName melibatkan situasi di mana kelas Java hanya memiliki metode getter untuk bidang. Dalam situasi ini Generator Pengikatan ingin membuat properti tulis-saja, sesuatu yang tidak disarankan dalam .NET. Cuplikan berikut menunjukkan cara "menghapus" properti .NET dengan mengatur propertyName ke string kosong:

<attr path="/api/package[@name='org.java_websocket.handshake']/class[@name='HandshakeImpl1Client']/method[@name='setResourceDescriptor' 
    and count(parameter)=1 
    and parameter[1][@type='java.lang.String']]" 
    name="propertyName"></attr>
<attr path="/api/package[@name='org.java_websocket.handshake']/class[@name='HandshakeImpl1Client']/method[@name='getResourceDescriptor' 
    and count(parameter)=0]" 
    name="propertyName"></attr>

Perhatikan bahwa metode setter dan getter masih akan dibuat oleh Generator Pengikatan.

pengirim

Menentukan parameter metode mana yang harus menjadi sender parameter ketika metode dipetakan ke peristiwa. Nilai dapat berupa true atau false. Contohnya:

<attr path="/api/package[@name='android.app']/
    interface[@name='TimePickerDialog.OnTimeSetListener']/
    method[@name='onTimeSet']/
    parameter[@name='view']" 
    name="sender">true</ attr>

Visibilitas

Atribut ini digunakan untuk mengubah visibilitas kelas, metode, atau properti. Misalnya, mungkin perlu untuk mempromosikan protected metode Java sehingga pembungkus C# yang sesuai adalah public:

<!-- Change the visibility of a class -->
<attr path="/api/package[@name='namespace']/class[@name='ClassName']" name="visibility">public</attr>

<!-- Change the visibility of a method --> 
<attr path="/api/package[@name='namespace']/class[@name='ClassName']/method[@name='MethodName']" name="visibility">public</attr>

EnumFields.xml dan EnumMethods.xml

Ada kasus di mana pustaka Android menggunakan konstanta bilangan bulat untuk mewakili status yang diteruskan ke properti atau metode pustaka. Dalam banyak kasus, berguna untuk mengikat konstanta bilangan bulat ini ke enum di C#. Untuk memfasilitasi pemetaan ini, gunakan file EnumFields.xml dan EnumMethods.xml dalam proyek pengikatan Anda.

Menentukan Enum menggunakan EnumFields.xml

File EnumFields.xml berisi pemetaan antara konstanta Java int dan C# enums. Mari kita ambil contoh enum C# berikut yang dibuat untuk sekumpulan int konstanta:

<mapping jni-class="com/skobbler/ngx/map/realreach/SKRealReachSettings" clr-enum-type="Skobbler.Ngx.Map.RealReach.SKMeasurementUnit">
    <field jni-name="UNIT_SECOND" clr-name="Second" value="0" />
    <field jni-name="UNIT_METER" clr-name="Meter" value="1" />
    <field jni-name="UNIT_MILIWATT_HOURS" clr-name="MilliwattHour" value="2" />
</mapping>

Di sini kita telah mengambil kelas SKRealReachSettings Java dan mendefinisikan enum C# yang disebut SKMeasurementUnit di namespace Skobbler.Ngx.Map.RealReach. Entri field menentukan nama konstanta Java (contoh UNIT_SECOND), nama entri enum (contoh Second), dan nilai bilangan bulat yang diwakili oleh kedua entitas (misalnya 0).

Menentukan Metode Getter/Setter menggunakan EnumMethods.xml

File EnumMethods.xml memungkinkan perubahan parameter metode dan mengembalikan jenis dari konstanta Java int ke C# enums. Dengan kata lain, ia memetakan pembacaan dan penulisan enum C# (didefinisikan dalam file EnumFields.xml) ke konstanta get dan set metode Javaint.

Mengingat enum yang SKRealReachSettings ditentukan di atas, file EnumMethods.xml berikut akan menentukan getter/setter untuk enum ini:

<mapping jni-class="com/skobbler/ngx/map/realreach/SKRealReachSettings">
    <method jni-name="getMeasurementUnit" parameter="return" clr-enum-type="Skobbler.Ngx.Map.RealReach.SKMeasurementUnit" />
    <method jni-name="setMeasurementUnit" parameter="measurementUnit" clr-enum-type="Skobbler.Ngx.Map.RealReach.SKMeasurementUnit" />
</mapping>

Baris pertama method memetakan nilai pengembalian metode Java getMeasurementUnit ke SKMeasurementUnit enum. Baris kedua method memetakan parameter pertama dari enum ke setMeasurementUnit enum yang sama.

Dengan semua perubahan ini, Anda dapat menggunakan kode berikut di Xamarin.Android untuk mengatur MeasurementUnit:

realReachSettings.MeasurementUnit = SKMeasurementUnit.Second;

Ringkasan

Artikel ini membahas bagaimana Xamarin.Android menggunakan metadata untuk mengubah definisi API dari format Google AOSP. Setelah mencakup perubahan yang dimungkinkan menggunakan Metadata.xml, ia memeriksa batasan yang ditemui saat mengganti nama anggota dan menyajikan daftar atribut XML yang didukung, yang menjelaskan kapan setiap atribut harus digunakan.