برنامج تضمين المكتبة لواجهات برمجة تطبيقات Android جزء من حزمة تطوير ألعاب Android.

برنامج تضمين المكتبة هو أداة سطر أوامر (CLI) تنشئ لغة C رمز برنامج تضمين لواجهات برمجة تطبيقات Android المكتوبة بلغة Java. يمكنك استخدام هذه الصفحة تعليمة برمجية في تطبيقات Android الأصلية لاستدعاء واجهات برمجة تطبيقات Java دون الحاجة إلى لإنشاء Java Native Interface أو JNI. يمكن لهذه الأداة تبسيط عملية التطوير تطبيقات Android المكتوبة بشكل أساسي بلغة C أو C++.

تعمل الأداة عن طريق إنشاء رمز C للرموز العامة الموجودة في أو ملفات أرشيف Java (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) {...}
}

يتطلب مشروعك C إنشاء برنامج تضمين أصلي لهذا JAR، ما يتيح لك تفعيل البرنامج. لتطبيق Android الأصلي لتتمكن من استدعائه أثناء وقت التشغيل. إنشاء هذه التعليمة البرمجية باستخدام برنامج تضمين بالأمر التالي:

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]

يمكنك التحكم في سلوك الإخراج هذا باستخدام config.

دورة حياة العنصر

يتم تمثيل كائنات Java في التعليمة البرمجية C على شكل مؤشرات مبهمة، تسمى برامج تضمين. يدير برنامج التضمين مرجع JNI لكائن Java المقابل. يمكن لبرنامج تضمين أن يتم إنشاؤه في السيناريوهات التالية:

  • من خلال إحاطة مرجع JNI الحالي باستدعاء الدالة MyClass_wrapJniReference(jobject jobj) الدالة لا يحصل على ملكية المرجع المقدَّم، بل ينشئ المرجع الخاص به مرجع JNI العالمي.
  • عن طريق إنشاء كائن جديد، يعادل استدعاء دالة إنشائية في جافا: 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. تقدِّم الأمثلة في الأقسام التالية سياق حول الطريقة التي قد تنشئ بها الأداة لغة C من خلال رمز Java. ملاحظة: في مقتطفات الرمز، تتضمّن الأمثلة التالية رمز C/C++ وJavaScript. والمقتطفات. تهدف هذه المقتطفات فقط إلى توضيح كيفية استخدام يُنشئ تعليمة برمجية لكل موقف معين.

صفوف

يتم تمثيل الفئات في شكل مؤشرات مبهمة في C:

ملفات المصدر C/C++

typedef struct MyClass_ MyClass;

Java

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

Java

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

Java

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;

Java

public class MyClass {
  public class InnerClass {...}
}

طرق الفئة الداخلية

يتم تمثيل طرق الفئة الداخلية على النحو التالي:

ملفات المصدر C/C++

bool MyClass_InnerClass_setValue(MyClass_InnerClass* my_class_inner_class_instance, int32_t value);

Java

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

Java

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

Java

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;

الدعم

إذا واجهت مشكلة في برنامج تضمين المكتبة، يُرجى إعلامنا بذلك.

تصفُّح الأخطاء الإبلاغ عن خطأ
الهندسة
الوثائق