برنامج تضمين المكتبة لواجهات برمجة تطبيقات Android جزء من مجموعة أدوات تطوير الألعاب من Android.
برنامج تضمين المكتبة هو أداة سطر أوامر (CLI) تنشئ رمز برنامج C-language لواجهات برمجة تطبيقات Android المكتوبة بلغة Java. يمكنك استخدام هذا الرمز في تطبيقات Android المحلية للاتصال بواجهات برمجة تطبيقات Java بدون الحاجة إلى إنشاء واجهة Java Native أو JNI. يمكن لهذه الأداة تبسيط تطوير تطبيقات Android التي تم إنشاؤها بشكل أساسي باللغة C أو C++.
تعمل الأداة من خلال إنشاء رمز C للرموز العلنية المضمّنة في ملفات Java Archive (JAR) أو الصفوف المحددة في ملف إعداد الأداة، أو كليهما. ولا تحل الرموز التي يتم إنشاؤها باستخدام الأداة محل واجهات برمجة تطبيقات Java، بل تعمل كجسر بين الرمز C ورمز Java. لا يزال تطبيقك يتطلّب تضمين مكتبات Java التي يتم تضمينها في مشروعك.
تنزيل
نزِّل أرشيف برنامج تضمين المكتبة وفكِّر المحتوى الخاص به في الدليل الذي تختاره.
بناء الجملة
تحتوي أداة برنامج تضمين المكتبة على بنية سطر الأوامر التالية:
java -jar lw.jar \
[-i jar-file-to-be-wrapped] \
[-o output-path] \
[-c config-file] \
[-fa allow-list-file] \
[-fb block-list-file] \
[--skip_deprecated_symbols]
المعلَمة | الوصف |
---|---|
-i jar-file-to-be-wrapped |
ملف JAR لإنشاء برنامج تضمين C. يمكن تحديد ملفات JAR متعددة، على سبيل المثال: -i first_library.jar -i second_library.jar...
|
-o output-path |
موقع نظام الملفات للرمز الذي تم إنشاؤه. |
-c config-file |
مسار نظام الملفات إلى ملف إعداد برنامج تضمين المكتبة. ولمعرفة التفاصيل، يُرجى الاطِّلاع على قسم الضبط. |
-fa allow-list-file |
هو مسار إلى ملف فلتر حيث يمكنك تحديد رموز للأداة التي يتم لفّها. ولمزيد من التفاصيل، اطّلِع على قسم الفلتر. |
-fb block-list-file |
مسار إلى ملف فلتر يتضمن رموزًا مستبعدة من الالتفاف. ولمزيد من التفاصيل، يُرجى الاطّلاع على قسم الفلتر. |
--skip_deprecated_symbols |
يتم تعليم أداة التضمين لتخطّي الرموز @موقوف. |
ملف إعداد المغلّف
إنّ إعداد برنامج تضمين المكتبة هو ملف JSON يسمح لك بالتحكّم في عملية إنشاء الرمز. يستخدم الملف البنية التالية.
{
// An array of type-specific configs. A type config is useful when a user wants to map
// a Java type to a manually defined C type without generating the code. For example, when a developer
// has their own implementation of the "java.lang.String" class, they can tell the generator to use it
// instead of generating it.
"type_configs": [
{
// [Required] Name of a fully qualified Java type.
"java_type": "java.lang.String",
// The C type that the java_type will be mapped to.
"map_to": "MyOwnStringImplementation",
// A header file that contains the declaration of the "map_to" type.
"source_of_definition": "my_wrappers/my_own_string_implementation.h",
// Controls if a value should be passed by pointer or value.
"pass_by_value": false
}
],
// An array of package-specific configs.
"package_configs": [
{
// [Required] A name of a Java package that this section regards. A wildchar * can be used at the
// end of the package name to apply this config to all packages whose name starts with this value.
"package_name": "androidx.core.app*",
// A subdirectory relative to the root directory where the generated code will be located.
"sub_directory": "androidx_generated/",
// If true, the generated file structure reflects the package name. For example, files generated
// for the package com.google.tools will be placed in the directory com/google/tools/.
"file_location_by_package_name": true,
// A prefix added to all class names from this package.
"code_prefix": "Gen",
// A prefix added to all generated file names from this package.
"file_prefix": = "gen_"
}
],
// An array of manually defined classes for wrapping. Defining classes manually is useful when a
// jar file with desired classes are not available or a user needs to wrap just a small part of an SDK.
"custom_classes": [
{
// [Required] A fully-qualified Java class name. To define inner class, use symbol "$", for example
// "class com.example.OuterClass$InnerClass".
"class_name": "class java.util.ArrayList<T>",
// List of methods.
"methods": [
"ArrayList()", // Example of a constructor.
"boolean add(T e)", // Example of a method that takes a generic parameter.
"T get(int index)", // Example of a method that returns a generic parameter.
"int size()" // Example of parameterless method.
]
},
]
}
فلترة الملفات
قد يكون من المفيد استبعاد بعض الرموز من ملفات JAR التي تنوي لفّها. يمكنك تحديد ملف فلتر في الإعدادات لاستبعاد الرموز. ملف الفلتر هو ملف نصي بسيط يحدد كل سطر رمزًا لفّه. تستخدم ملفات الفلاتر البنية التالية:
java-symbol-name java-jni-type-signature
في ما يلي مثال على ملف فلتر:
# Class filter
java.util.ArrayList Ljava.util.ArrayList;
# Method filter
java.util.ArrayList.lastIndexOf (Ljava.lang.Object;)I
# Field filter
android.view.KeyEvent.KEYCODE_ENTER I
ستوفّر لك الضبط ملف فلتر يحدّد الرموز المسموح بها
باستخدام المَعلمة -fa
، والرموز المحظورة باستخدام المَعلمة -fb
. ويمكن استخدام كلتا المعلّمتَين في الوقت نفسه. وفي حال تقديم
الفلترَين، سيتم لف رمز عند تحديده في ملف فلتر السماح
وعدم وجوده في ملف فلتر الحظر.
مثال على السيناريو
لنفترض أنك تحتاج إلى لف ملف JAR ChatLibrary.jar
الذي يحتوي على الصف التالي:
public class ChatManager {
public static void sendMessage(int userId, String message) {...}
}
يتطلّب مشروعك أنشئ هذا الرمز باستخدام برنامج تضمين المكتبة باستخدام الأمر التالي:
java -jar lw.jar -i ChatLibrary.jar -o ./generated_code/
ينشئ الأمر السابق رمز المصدر C إلى الدليل
./generated_code
. يحتوي الملف chat_manager.h
الذي تم إنشاؤه على رمز مشابه لما يلي، ما يتيح لك الاتصال بالمكتبة في مشروعك:
#include "java/lang/string.h"
typedef struct ChatManager_ ChatManager;
void ChatManager_sendMessage(int32_t user_id, String* message);
للاطّلاع على مثال مفصّل، راجِع دليل برنامج تضمين المكتبة.
تفاصيل الأداة
تقدم الأقسام التالية معلومات تفصيلية عن وظيفة برنامج تضمين المكتبة.
بنية دليل الإخراج
يمكن العثور على جميع ملفات المصدر وعنوان C في الأدلة الفرعية التي تعكس
اسم حزمة فئة Java الملتفة. على سبيل المثال، يتم إنشاء رمز برنامج تضمين
JAR java.lang.Integer
المحدد إلى الدليل
./java/lang/integer.[h/cc]
.
يمكنك التحكم في سلوك الناتج هذا باستخدام ملف الضبط الخاص بالأداة.
مراحل نشاط العنصر
يتم تمثيل كائنات Java في الرمز C كمؤشرات مبهمة تُعرف باسم برامج التضمين. يدير برنامج التضمين مرجع JNI لكائن Java مطابقًا له. يمكن إنشاء برنامج تضمين في السيناريوهات التالية:
- من خلال ربط مرجع حالي في JNI من خلال طلب الدالة
MyClass_wrapJniReference(jobject jobj)
. لا تأخذ الدالة ملكية المرجع المقدّم، ولكنها تنشئ مرجع JNI العمومي الخاص بها. - من خلال إنشاء عنصر جديد يعادل استدعاء أداة إنشاء في Java:
MyClass_construct()
- من خلال عرض برنامج تضمين جديد من إحدى الدوال، على سبيل المثال:
Score* Leaderboard_getScore(Leaderboard* instance, String* leaderboard_name)
يجب محو جميع برامج التضمين إذا لم تعُد قيد الاستخدام. لإجراء ذلك، يمكنك طلب الدالة destroy()
MyClass_destroy(MyClass* instance)
المخصّصة.
في الدوال التي تعرض برامج تضمين، يتم تخصيص ذكرى جديدة لكل طلب، حتى لو كانت برامج التضمين تمثّل مثيل Java نفسه.
على سبيل المثال، عندما تعرض الطريقة Java Singleton.getInstance()
المثيل نفسه دائمًا، ستنشئ الدالة المكافئة على الجانب C مثيلاً جديدًا من برنامج تضمين لمثيل Java نفسه:
Singleton* singleton_a = Singleton_getInsance();
Singleton* singleton_b = Singleton_getInsance();
// singleton_a and singleton_b are different pointers, even though they represent the same Java instance.
التعامل مع الصفوف غير المرجعية
عندما يتعذّر العثور على فئة في ملف JAR مقدّم، ينشئ برنامج تضمين النص المبسَّط تنفيذًا أساسيًا يتألف من مؤشر مبهم والطرق التالية:
wrapJniReference()
getJniReference()
destroy()
تفاصيل إنشاء الرمز
عند التشغيل، ينشئ برنامج تضمين المكتبة رمز C استنادًا إلى الرموز العلنية في ملفات JAR التي توفّرها. قد يُظهر الرمز C الذي تم إنشاؤه اختلافات عن رمز Java الملتف. على سبيل المثال، لا يتوافق بروتوكول C مع ميزات مثل OOP أو الأنواع العامة أو التحميل الزائد للطرق أو ميزات Java أخرى.
قد يختلف الرمز C الذي يتم إنشاؤه الذي يوضّح هذه المواقف عن نوع الرمز الذي يتوقّعه مطوّرو البرامج. تقدّم الأمثلة في الأقسام التالية سياقًا حول كيفية إنشاء الأداة C من رمز Java. ملاحظة: في مقتطفات الرمز، تتضمن الأمثلة التالية مقتطفات C/C++ ومقتطفات رمز Java. وتهدف هذه المقتطفات فقط إلى توضيح آلية الأداة في إنشاء رمز لكل موقف.
الفئات
يتم تمثيل الفئات على أنها مؤشّرات مبهمة في C:
C/C++
typedef struct MyClass_ MyClass;
جافا
public class MyClass { ... }
وتتم الإشارة إلى مثيلات المؤشرات المبهمة باعتبارها برامج تضمين. تُنشئ أداة التضمين
دوال دعم إضافية لكل صف. بالنسبة إلى المثال السابق
للفئة MyClass
، يتم إنشاء الدوال التالية:
// Wraps a JNI reference with MyClass. The 'jobj' must represent MyClass on the Java side.
MyClass* MyClass_wrapJniReference(jobject jobj);
// Return JNI reference associated with the 'MyClass' pointer.
jobject MyClass_getJniReference(const MyClass* object);
// Destroys the object and releases underlying JNI reference.
void MyClass_destroy(const MyClass* object);
الشركات المصنِّعة
يتم تمثيل الفئات التي تحتوي على طرق إنشاء عامة أو تلقائية باستخدام دوال خاصة:
C/C++
MyClass* MyClass_construct(String* data);
جافا
public class MyClass {
public MyClass(String data) { ... }
}
الطرق
يتم تمثيل الطرق كدوال عادية. يحتوي اسم الدالة على
اسم الفئة الأصلي. تحتوي الدوال التي تمثّل طرق المثيل غير الثابتة
على أنها المَعلمة الأولى كمؤشر على بنية تمثّل كائن Java، يتم
استدعاء الدالة نيابةً عنه. وتتشابه هذه الطريقة مع مؤشر this
.
C/C++
Result* MyClass_doAction(const MyClass* my_class_instance, int32_t action_id, String* data);
int32_t MyClass_doAction(int32_t a, int32_t b);
جافا
public class MyClass {
public Result doAction(int actionId, String data) { ... }
public static int doCalculations(int a, int b) { ... }
}
دروس داخلية
يتم تقديم الطبقات الداخلية بشكل وثيق إلى الفئات العادية، باستثناء اسم البنية C المقابل لها تحتوي على أسماء السلاسل الخارجية:
C/C++
typedef struct MyClass_InnerClass_ MyClass_InnerClass;
جافا
public class MyClass {
public class InnerClass {...}
}
أساليب الصف الداخلي
يتم تمثيل طرق الصف الداخلية على النحو التالي:
C/C++
bool MyClass_InnerClass_setValue(MyClass_InnerClass* my_class_inner_class_instance, int32_t value);
جافا
public class MyClass {
public class InnerClass {
public boolean setValue(int value) { ... }
}
}
الأنواع العامة
لا يغطي برنامج تضمين المكتبة الأنواع العامة بشكل مباشر. بدلاً من ذلك، تعمل الأداة على إنشاء برامج تضمين للنسخ الافتراضية من النوع العام فقط.
على سبيل المثال، عند توفُّر فئة MyGeneric<T>
في واجهة برمجة تطبيقات، ويتوفّر دقيقتَان من أمثلة هذه الفئة، مثل MyGeneric<Integer>
و
MyGeneric<String>
، يتم إنشاء برامج تضمين لهذه الاقتراحات الافتراضية. هذا يعني أنه لا يمكنك إنشاء مثيلات جديدة من النوع MyGeneric<T>
باستخدام أنواع ضبط مختلفة. اطّلِع على المثال التالي:
C/C++
// result.h
typedef struct Result_Integer_ Result_Integer;
typedef struct Result_Float_ Result_Float;
Integer* Result_Integer_getResult(const Result_Integer* instance);
Float* Result_Float_getResult(const Result_Float* instance);
// data_processor.h
typedef struct DataProcessor_ DataProcessor;
Result_Integer* DataProcessor_processIntegerData(const DataProcessor* instance);
Result_Float* DataProcessor_processFloatData(constDataProcessor* instance);
جافا
public class Result<T> {
public T getResult();
}
public class DataProcessor {
public Result<Integer> processIntegerData();
public Result<Float> processFloatData();
}
تنفيذ الواجهات
تنفيذ واجهة C من خلال طلب implementInterface()
وتوفير دالة رد اتصال لكل طريقة واجهة. يمكن تنفيذ الواجهات فقط
بهذه الطريقة، لا تتوفّر الصفوف والصفوف المجرّدة. اطّلِع على المثال التالي:
C/C++
// observer.h
typedef struct Observer_ Observer;
typedef void (*Observer_onAction1Callback)();
typedef void (*Observer_onAction2Callback)(int32_t data);
Observer* Observer_implementInterface(
Observer_onAction1Callback observer_on_action1_callback,
Observer_onAction2Callback observer_on_action2_callback);
جافا
public interface Observer {
void onAction1();
void onAction2(int data);
}
public class Subject {
public void registerObserver(Observer observer);
}
مثال على الاستخدام:
void onAction1() {
// Handle action 1
}
void onAction2(int32_t data) {
// Handle action 2
}
Observer* observer = Observer_implementInterface(onAction1, onAction2);
Subject_registerObserver(subject, observer);
القيود
أداة تضمين برنامج المكتبة في الإصدار التجريبي. قد تواجه القيود التالية:
إنشاءات Java غير المتوافقة
لا يتوافق الإصدار التجريبي من برنامج تضمين المكتبة مع الإصدارات التالية:
تحميل زائد على الطريقة
لا تسمح اللغة C بتعريف دالتَين بالاسم نفسه. إذا تم استخدام طريقة تحميل زائد في الصف، لن يتم تجميع رمز C الذي تم إنشاؤه. يتمثل الحل البديل في استخدام طريقة واحدة فقط مع مجموعة كافية من المعلَمات. يمكن فلترة الدوال المتبقية باستخدام الفلاتر. ينطبق هذا أيضًا على الشركات المصنّعة.
الطرق النموذجية
الحقلان غير
static final int
وstatic final String
المصفوفات
قد تحدث تضاربات في الأسماء
قد تحدث حالات تعارض في الأسماء في حالات نادرة جدًا بسبب طريقة تمثيل فئات Java في الرمز C. على سبيل المثال، يتم تمثيل الفئة Foo<Bar>
والفئة الداخلية Bar
داخل الفئة Foo
بالرمز نفسه في C:
typedef struct Foo_Bar_ Foo_Bar;
الدعم
إذا عثرت على مشكلة في برنامج تضمين المكتبة، يُرجى إعلامنا بذلك.
تصفُّح الأخطاء | الإبلاغ عن خطأ |
---|---|
الهندسة | bug_report |
المستندات | bug_report |