دليل برنامج تضمين المكتبة

يوضِّح هذا الدليل طريقة استخدام برنامج تضمين مكتبة واجهة برمجة تطبيقات Android. المكتبة أداة سطر أوامر برنامج تضمين تنشئ رمز برنامج تضمين لغة C للغة Java Android تتيح لك واجهات برمجة التطبيقات دمج مكتبات Java في تطبيقات Android الأصلية بلغة C/C++. لمزيد من التفاصيل حول برنامج تضمين المكتبة، راجع برنامج تضمين المكتبة لواجهات برمجة تطبيقات Android

يوضح هذا الدليل المفصّل كيفية استخدام أداة التضمين لدمج تحويل مكتبة Java إلى تطبيق Android أصلي. على سبيل المثال، يتناول هذا الدليل بعض الأغراض. دمج مكتبة الإشعارات لحزمة androidx.core.app. يمكنك الاطّلاع على إنشاء إشعار للحصول على مزيد من المعلومات حول هذه المكتبة.

المتطلّبات الأساسية

من المفترض أن يكون لديك مشروع Android حالي في هذا الدليل. وكذلك نظام تصميم Gradle. إذا لم يكن لديك مشروع حالي، يمكنك إنشاء جديدة في "استوديو Android" باستخدام نموذج ++C الأصلية.

يستخدم الرمز النموذجي في هذا الدليل جذر الدليل my_project/. مدمج مع المحتوى الرمز الموجود في my_project/app/src/main/cpp/، الدليل التلقائي مشاريع "استوديو Android"

إذا لم تكن لديك أداة تضمين المكتبة من قبل، يمكنك تنزيل ملف تعريف الارتباط وفك ضغطه. حزمة إلى الدليل الذي تختاره. تتطلّب أداة CLI هذه "وقت تشغيل Java" البيئة (JRE).

إنشاء رمز برمجي أصلي

وعند دمج مكتبة Java، استخدِم أداة التضمين من أجل إنشاء برنامج تضمين رمز أصلي تتمثل الخطوة الأولى في تهيئة برنامج تضمين.

إنشاء إعداد برنامج تضمين

أنشئ ملفات تهيئة لبرنامج تضمين المكتبة للتحكم في المُخرج من منشئ الرمز الأصلي. تتيح لك إحدى ميزات هذا الملف تحديد الفئات والطرق لإنشاء رمز تضمين.

نظرًا لعدم توفُّر طرق كثيرة لتشغيل مكتبة الإشعارات، يمكنك يمكنك تحديدها مباشرةً في القسم custom_classes. إنشاء حساب جديد مورد واحد (config.json) في أي مكان في مشروعك لتحديد الطرق. على سبيل المثال: يمكنك إنشاء my_project/library_wrapper/config.json ولصق ما يلي نموذج الضبط:

{
  "custom_classes": [
    {
      "class_name": "class java.lang.CharSequence"
    },
    {
      "class_name": "class java.lang.Object",
      "methods": [
        "java.lang.String toString()"
      ]
    },
    {
      "class_name": "class java.lang.String"
    },
    {
      "class_name": "class android.content.Context",
      "methods": [
        "java.lang.Object getSystemService(java.lang.String name)"
      ]
    },
    {
      "class_name": "class android.app.Notification"
    },
    {
      "class_name": "class android.app.NotificationManager",
      "methods": [
        "void createNotificationChannel(android.app.NotificationChannel channel)"
      ]
    },
    {
      "class_name": "class android.app.NotificationChannel",
      "methods": [
        "NotificationChannel(java.lang.String id, java.lang.CharSequence name, int importance)",
        "void setDescription(java.lang.String description)"
      ]
    },
    {
      "class_name": "class androidx.core.app.NotificationCompat"
    },
    {
      "class_name": "class androidx.core.app.NotificationCompat$Builder",
      "methods": [
        "Builder(android.content.Context context, java.lang.String channelId)",
        "androidx.core.app.NotificationCompat$Builder setContentText(java.lang.CharSequence text)",
        "androidx.core.app.NotificationCompat$Builder setContentTitle(java.lang.CharSequence title)",
        "androidx.core.app.NotificationCompat$Builder setSmallIcon(int icon)",
        "androidx.core.app.NotificationCompat$Builder setPriority(int pri)",
        "android.app.Notification build()"
      ]
    },
    {
      "class_name": "class androidx.core.app.NotificationManagerCompat",
      "methods": [
        "static androidx.core.app.NotificationManagerCompat from(android.content.Context context)",
        "void notify(int id, android.app.Notification notification)"
      ]
    }
  ]
}

في العينة السابقة، تقوم بتعريف فئات وأساليب Java بشكل مباشر تتطلب رمز برنامج تضمين أصلي.

تشغيل برنامج تضمين المكتبة

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

java -jar lw.jar \
  -o "my_project/app/src/main/cpp/native_wrappers" \
  -c "my_project/library_wrapper/config.json"

في العيّنة السابقة، يمكنك استخدام المَعلمة -c لتحديد برنامج تضمين. موقع التهيئة والمعلمة -o لتحديد دليل الرمز الذي تم إنشاؤه. بعد تشغيل الأداة، من المفترض أن يكون لديك الآن الكود الذي تم إنشاؤه لاستدعاء واجهة برمجة التطبيقات للإشعارات المستندة إلى Java من تطبيقك الأصلي

تنفيذ الإشعارات الأصلية

في هذا القسم، يتم دمج مكتبة إشعارات Android في أصلي باستخدام رمز برنامج تضمين تم إنشاؤه. تتمثل الخطوة الأولى في تحديث مورد gradle.build على مستوى التطبيق في المشروع (my_project/app/gradle.build).

تعديل درجات الحرارة في "gradle.build"

  1. "مبادرة أخبار Google" هي مكتبة دعم تتطلّبها رمز الحزمة الذي تم إنشاؤه. جميع المشاريع باستخدام التعليمات البرمجية التي تم إنشاؤها، يجب أن يشير إلى هذه المكتبة. للإشارة إلى هذه المكتبة، أضِف السطر التالي إلى القسم dependencies من build.gradle:

    implementation 'com.google.android.gms:play-services-gni-native-c:1.0.0-beta2'
    
  2. لتفعيل دعم prefab، أضِف الرمز التالي إلى القسم android:

    buildFeatures {
      prefab true
    }
    
  3. لضبط cmake، استخدِم إعدادات cmake التالية في القسم android/defaultConfig:

    externalNativeBuild {
      cmake {
          arguments '-DANDROID_STL=c++_shared'
      }
    }
    

يجب أن تكون إعدادات "build.gradle" المكتملة مشابهة لما يلي:

android {
    ...

    buildFeatures {
        prefab true
    }

    defaultConfig {
        ...

        externalNativeBuild {
            cmake {
                arguments '-DANDROID_STL=c++_shared'
            }
        }
    }
}

dependencies {
    ...
    implementation 'com.google.android.gms:play-services-gni-native-c:1.0.0-beta2'
    ...
}

تعديل CMakeLists

  1. إضافة مكتبة "مبادرة أخبار Google" إلى CMakeLists.txt الخاص بمشروعك (my_project/app/src/main/cpp/CMakeLists.txt) عن طريق إضافة السطر التالي في المستوى الأعلى من الملف:

    find_package(com.google.android.gms.gni.c REQUIRED CONFIG)
    
  2. أضِف السطر التالي إلى القسم target_link_libraries:

    PUBLIC com.google.android.gms.gni.c::gni_shared
    
  3. أضف مرجعًا إلى التعليمة البرمجية التي تم إنشاؤها عن طريق إضافة السطر التالي في المستوى الأعلى من الملف:

    file(GLOB_RECURSE native_wrappers CONFIGURE_DEPENDS "native_wrappers/*.cpp" "native_wrappers/*.cc")
    
  4. أضِف هذه السطور بالقرب من نهاية الملف:

    include_directories(./native_wrappers/c)
    include_directories(./native_wrappers/cpp)
    

يجب أن يكون مورد CMakeLists.txt المعدَّل مشابهًا للنموذج التالي:

cmake_minimum_required(VERSION 3.18.1)

project("my_project")

file(GLOB_RECURSE native_wrappers CONFIGURE_DEPENDS "native_wrappers/*.cpp" "native_wrappers/*.cc")

add_library(
        my_project
        SHARED
        native-lib.cpp
        ${native_wrappers}
        )

find_library(
        log-lib
        log)

find_package(com.google.android.gms.gni.c REQUIRED CONFIG)

target_link_libraries(
        my_project
        PUBLIC com.google.android.gms.gni.c::gni_shared
        ${log-lib})

include_directories(./native_wrappers/c)
include_directories(./native_wrappers/cpp)

تطبيق منطق الإشعارات

  1. افتح أو أنشئ الملف المصدر الذي تريد تطبيق الإشعار عليه. والإمكانات. في هذا الملف، ضمِّن ملف العنوان gni.h وحدِّد الدالة ShowNativeNotification() الجديدة:

    #include "gni/gni.h"
    
    void ShowNativeNotification(JNIEnv *env, jobject main_activity, int icon_id) {
      // Get the JavaVM from the JNIEnv.
      JavaVM *java_vm;
      env->GetJavaVM(&java_vm);
    
      // Initialize the GNI runtime. This function needs to be called before any
      // call to the generated code.
      GniCore_init(java_vm, main_activity);
    }
    
  2. تحديد القيم الثابتة الخاصة بالإشعارات والإشعار دالتا المعالِج CharSequenceFromCString() وCreateNotification():

    C

    const int32_t IMPORTANCE_HIGH = 4;  // NotificationManager.IMPORTANCE_HIGH
    const int32_t PRIORITY_MAX = 2;  // NotificationCompat.PRIORITY_MAX
    const int32_t NOTIFICATION_ID = 123;  // User defined notification id.
    
    // Convert a C string into CharSequence.
    CharSequence *CharSequenceFromCString(const char *text) {
       String *string = String_fromCString(text);
       // Cast String to CharSequence. In Java, a String implements CharSequence.
       CharSequence *result = GNI_CAST(CharSequence, String, string);
       // Casting creates a new object, so it needs to be destroyed as normal.
       String_destroy(string);
       return result;
    }
    
    // Create a notification.
    Notification *
    CreateNotification(Context *context, String *channel_id,
                       const char *title, const char *content,
                       int32_t icon_id) {
       // Convert C strings to CharSequence.
       CharSequence *title_chars = CharSequenceFromCString(title);
       CharSequence *content_chars = CharSequenceFromCString(content);
    
       // Create a NotificationCompat.Builder and set all required properties.
       NotificationCompat_Builder *notification_builder =
           NotificationCompat_Builder_construct(context, channel_id);
       NotificationCompat_Builder_setContentTitle(notification_builder,
                                                  title_chars);
       NotificationCompat_Builder_setContentText(notification_builder,
                                                 content_chars);
       NotificationCompat_Builder_setSmallIcon(notification_builder, icon_id);
       NotificationCompat_Builder_setPriority(notification_builder,
                                              PRIORITY_MAX);
    
       // Build a notification.
       Notification *notification =
           NotificationCompat_Builder_build(notification_builder);
    
       // Clean up allocated objects.
       NotificationCompat_Builder_destroy(notification_builder);
       CharSequence_destroy(title_chars);
       CharSequence_destroy(content_chars);
    
       return notification;
    }
    

    C++‎

    const int32_t IMPORTANCE_HIGH = 4;  // NotificationManager.IMPORTANCE_HIGH
    const int32_t PRIORITY_MAX = 2;  // NotificationCompat.PRIORITY_MAX
    const int32_t NOTIFICATION_ID = 123;  // User defined notification id.
    
    // Convert a C string into CharSequence.
    CharSequence *CharSequenceFromCString(const char *text) {
       String *string = String_fromCString(text);
       // Cast String to CharSequence. In Java, a String implements CharSequence.
       CharSequence *result = new CharSequence(string->GetImpl());
       // Casting creates a new object, so it needs to be destroyed as normal.
       String::destroy(string);
       return result;
    }
    
    // Create a notification.
    Notification&
    CreateNotification(Context *context, String *channel_id, const char *title,
                       const char *content, int32_t icon_id) {
       // Convert C strings to CharSequence.
       CharSequence *title_chars = CharSequenceFromCString(title);
       CharSequence *content_chars = CharSequenceFromCString(content);
    
       // Create a NotificationCompat.Builder and set all required properties.
    
       NotificationCompat::Builder *notification_builder = new NotificationCompat::Builder(*context, *channel_id);
       notification_builder->setContentTitle(*title_chars);
       notification_builder->setContentText(*content_chars);
       notification_builder->setSmallIcon(icon_id);
       notification_builder->setPriority(PRIORITY_MAX);
    
       // Build a notification.
       Notification& notification = notification_builder->build();
    
       // Clean up allocated objects.
       NotificationCompat::Builder::destroy(notification_builder);
       CharSequence::destroy(title_chars);
       CharSequence::destroy(content_chars);
    
       return notification;
    }
    

    تستخدم بعض وظائف مكتبة الإشعارات CharSequence بدلاً من String تتيح الدالة CharSequenceFromCString() التحويل بين هذه الكائنات. تستخدم الدالة CreateNotification() النسخة الملفوفة من Java NotificationCompat.Builder لإنشاء إشعار.

  3. أضِف منطقًا لإنشاء قناة إشعارات من خلال لصق ما يلي: الدالة، CreateNotificationChannel():

    C

    void CreateNotificationChannel(Context *context, String *channel_id) {
       CharSequence *channel_name = CharSequenceFromCString("channel name");
       String *channel_description = String_fromCString("channel description");
       String *system_service_name = String_fromCString("notification");
       NotificationChannel *channel =
           NotificationChannel_construct(channel_id, channel_name,
                                         IMPORTANCE_HIGH);
       NotificationChannel_setDescription(channel, channel_description);
    
       Object *notification_manager_as_object =
           Context_getSystemService(context, system_service_name);
       NotificationManager *notification_manager =
           GNI_CAST(NotificationManager, Object,
                    notification_manager_as_object);
    
       NotificationManager_createNotificationChannel(notification_manager,
                                                     channel);
    
       CharSequence_destroy(channel_name);
       String_destroy(channel_description);
       String_destroy(system_service_name);
       NotificationChannel_destroy(channel);
       Object_destroy(notification_manager_as_object);
       NotificationManager_destroy(notification_manager);
    }
    

    C++‎

    void CreateNotificationChannel(Context *context, String *channel_id) {
       CharSequence *channel_name = CharSequenceFromCString("channel name");
       String *channel_description = String_fromCString("channel description");
       String *system_service_name = String_fromCString("notification");
       NotificationChannel *channel =
           new NotificationChannel(*channel_id, *channel_name, IMPORTANCE_HIGH);
       channel->setDescription(*channel_description);
    
       Object& notification_manager_as_object =
           context->getSystemService(*system_service_name);
       NotificationManager *notification_manager =
           new NotificationManager(notification_manager_as_object.GetImpl());
    
       notification_manager->createNotificationChannel(*channel);
    
       CharSequence::destroy(channel_name);
       String::destroy(channel_description);
       String::destroy(system_service_name);
       NotificationChannel::destroy(channel);
       Object::destroy(&notification_manager_as_object);
       NotificationManager::destroy(notification_manager);
    }
    
  4. عدِّل الدالة ShowNativeNotification() التي أنشأتها سابقًا من أجل الاتصال بـ CreateNotificationChannel(). أضف التعليمة البرمجية التالية في نهاية ShowNativeNotification():

    C

    void ShowNativeNotification(JNIEnv *env, jobject main_activity, int icon_id) {
     // ...
    
     // Create a Context object by wrapping an existing JNI reference.
     Context *context = Context_wrapJniReference(main_activity);
    
     // Create a String object.
     String *channel_id = String_fromCString("new_messages");
    
     // Create a notification channel.
     CreateNotificationChannel(context, channel_id);
    
     // Create a notification with a given title, content, and icon.
     Notification *notification =
         CreateNotification(context, channel_id, "My Native Notification",
                            "Hello!", icon_id);
    
     // Create a notification manager and use it to show the notification.
     NotificationManagerCompat *notification_manager =
         NotificationManagerCompat_from(context);
     NotificationManagerCompat_notify(notification_manager, NOTIFICATION_ID,
                                      notification);
    
     // Destroy all objects.
     Context_destroy(context);
     String_destroy(channel_id);
     Notification_destroy(notification);
     NotificationManagerCompat_destroy(notification_manager);
    }
    

    C++‎

    void ShowNativeNotification(JNIEnv *env, jobject main_activity, int icon_id) {
       // Get the JavaVM from the JNIEnv.
       JavaVM *java_vm;
       env->GetJavaVM(&java_vm);
    
       // Initialize the GNI runtime. This function needs to be called before any
       // call to the generated code.
       GniCore::Init(java_vm, main_activity);
    
       // Create a Context object by wrapping an existing JNI reference.
       Context *context = new Context(main_activity);
    
       // Create a String object.
       String *channel_id = String_fromCString("new_messages");
    
       // Create a notification channel.
       CreateNotificationChannel(context, channel_id);
    
       // Create a notification with a given title, content, and icon.
       Notification& notification =
           CreateNotification(context, channel_id, "My Native Notification",
                              "Hello!", icon_id);
    
       // Create a notification manager and use it to show the notification.
       NotificationManagerCompat& notification_manager =
           NotificationManagerCompat::from(*context);
       notification_manager.notify(NOTIFICATION_ID, notification);
    
       // Destroy all objects.
       Context::destroy(context);
       String::destroy(channel_id);
       Notification::destroy(&notification);
       NotificationManagerCompat::destroy(&notification_manager);
    }   
  5. بعد تحديد منطقك، يمكنك تشغيل إشعار من خلال استدعاء ShowNativeNotification() في موقع مناسب في مشروعك.

تشغيل التطبيق

جمِّع وتنفيذ الرمز الذي يستدعي ShowNativeNotification(). تطبيق بسيط في أعلى شاشة الجهاز الاختباري.

إنشاء برامج تضمين من ملفات JAR

في المثال السابق، قمت بتحديد فئات Java يدويًا تتطلب رمزًا أصليًا في ملف تهيئة برنامج تضمين. بالنسبة للسيناريوهات التي يحتاجون إلى الوصول إلى أقسام كبيرة من واجهة برمجة التطبيقات، فإن توفير أحدهما أو المزيد من ملفات JAR للمكتبة إلى أداة التضمين. ينشئ برنامج تضمين بعد ذلك برامج تضمين جميع الرموز العامة التي يعثر عليها في JAR.

يلخّص المثال التالي واجهة برمجة التطبيقات Notifications API بالكامل من خلال توفير مكتبة. JAR.

الحصول على حِزم JAR المطلوبة

Reporting API هي جزء من حزمة "androidx.core" المتاحة من مستودع Google Maven. يمكنك تنزيل ملف المكتبة aar وفك ضغطه إلى دليل من اختيارك. حدِّد موقع الملف classes.jar.

يحتوي ملف classes.jar على العديد من الصفوف التي تتجاوز الإشعارات المطلوبة المكتبة. في حال توفير برنامج تضمين المكتبة باستخدام classes.jar فقط، ستتغيّر الأداة رمزًا أصليًا لكل فئة في JAR، وهو غير فعال غير ضرورية لمشروعنا. لحل هذه المشكلة، قدّم ملف تصفية إلى إعداد برنامج تضمين لحظر إنشاء الرموز بإشعار JAR الصفوف.

تحديد فلتر السماح

ملفات الفلاتر هي ملفات نص عادي تقدّمها لبرنامج تضمين المكتبة. التكوين. تسمح لك بتحديد الفئات التي يجب تضمينها (أو استبعادها) من ملفات JAR المقدَّمة إلى برنامج تضمين المكتبة.

في مشروعك، أنشِئ ملفًا بعنوان "allowed-symbols.txt" والصِقه في السطر التالي:

androidx.core.app.NotificationCompat*

عند استخدامه كفلتر سماح، يحدد الرمز السابق أن الرموز فقط التي يبدأ اسمها بـ androidx.core.app.NotificationCompat ويتم التفافها.

تشغيل برنامج تضمين المكتبة

افتح وحدة طرفية في دليل JAR وشغِّل الأمر التالي:

java -jar lw.jar \
 -i classes.jar \
 -o "./generated-jar" \
 -c "./config.json" \
 -fa allowed-symbols.txt \
 --skip_deprecated_symbols

ينشئ الأمر النموذج السابق رمز برنامج تضمين للفئات التي تمت فلترتها. إلى الدليل generated-jar/.

الدعم

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

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