คำแนะนำเกี่ยวกับ Wrapper ของคลังเพลง

คำแนะนำนี้จะอธิบายวิธีใช้ Wrapper ของไลบรารี Android API ห้องสมุด เครื่องมือบรรทัดคำสั่ง Wrapper จะสร้างโค้ด Wrapper ภาษา C สำหรับ Java Android API ที่ช่วยให้คุณผสานรวมไลบรารี Java เข้ากับแอป Android สำหรับ C/C++ ที่มาพร้อมเครื่อง ดูรายละเอียดเพิ่มเติมเกี่ยวกับ Wrapper ของไลบรารีได้ที่ Wrapper ของไลบรารีสำหรับ API ของ Android

คำแนะนำทีละขั้นตอนนี้จะสาธิตวิธีใช้เครื่องมือ Wrapper เพื่อผสานรวม ไลบรารี Java ลงในแอป Android ที่มาพร้อมเครื่อง คู่มือนี้มีตัวอย่างเพื่อวัตถุประสงค์ดังนี้ การผสานรวมไลบรารีการแจ้งเตือนของแพ็กเกจ androidx.core.app โปรดดูที่สร้างการแจ้งเตือนเพื่อเรียนรู้เพิ่มเติมเกี่ยวกับไลบรารีนี้

สิ่งที่ต้องมีก่อน

คู่มือนี้จะถือว่าคุณมีโปรเจ็กต์ Android ที่มาพร้อมเครื่องอยู่แล้ว และ ใช้ระบบบิลด์ Gradle หากยังไม่มี ให้สร้างโปรเจ็กต์ เวอร์ชันใหม่ใน Android Studio โดยใช้เทมเพลต C++ ดั้งเดิม

โค้ดตัวอย่างในคู่มือนี้ใช้รูทไดเรกทอรี my_project/ เนทีฟ รหัสอยู่ใน my_project/app/src/main/cpp/ ซึ่งเป็นไดเรกทอรีเริ่มต้นสำหรับ โปรเจ็กต์ Android Studio

หากคุณยังไม่มีเครื่องมือ Wrapper ของไลบรารี ให้ดาวน์โหลดและแตกไฟล์ ลงในไดเรกทอรีที่คุณเลือก เครื่องมือ CLI นี้ต้องใช้รันไทม์ของ Java Environment (JRE)

สร้างโค้ดเนทีฟ

เมื่อผสานรวมไลบรารี Java ให้ใช้เครื่องมือ Wrapper เพื่อ สร้าง Wrapper โค้ดแบบเนทีฟ ขั้นตอนแรกคือกำหนดค่า Wrapper

สร้างการกำหนดค่า Wrapper

คุณสามารถสร้างไฟล์การกำหนดค่า Wrapper ของไลบรารีเพื่อควบคุม เอาต์พุตของโปรแกรมสร้างโค้ดแบบเนทีฟ ฟีเจอร์หนึ่งของไฟล์นี้ช่วยให้คุณระบุ คลาสและวิธีสร้างโค้ด Wrapper

เนื่องจากการรวมสำหรับไลบรารีการแจ้งเตือนมีหลายวิธีด้วยกัน คุณสามารถ ให้ระบุในส่วน 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)"
      ]
    }
  ]
}

ในตัวอย่างก่อนหน้านี้ คุณจะประกาศคลาสและ Method ของ Java โดยตรงที่ ซึ่งต้องการรหัส Wrapper เนทีฟ

เรียกใช้ Wrapper ของไลบรารี

หลังจากกำหนดไฟล์การกำหนดค่า Wrapper คุณก็พร้อมที่จะใช้เครื่องมือเพื่อสร้าง ด้วยโค้ด Wrapper เนทีฟ เปิดเทอร์มินัลไปยังตำแหน่งที่คุณแตก Wrapper ของไลบรารี และเรียกใช้คำสั่งต่อไปนี้

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

ในตัวอย่างก่อนหน้านี้คุณใช้พารามิเตอร์ -c เพื่อระบุ Wrapper ตำแหน่งการกำหนดค่า และพารามิเตอร์ -o เพื่อกำหนดไดเรกทอรีโค้ดที่สร้างขึ้น หลังจากเรียกใช้เครื่องมือ ตอนนี้คุณควรมีโค้ดที่สร้างขึ้นซึ่งจำเป็นต้องใช้เพื่อเรียกใช้ API การแจ้งเตือนแบบ Java จากแอปที่มาพร้อมเครื่อง

ใช้การแจ้งเตือนดั้งเดิม

ในส่วนนี้ คุณได้ผสานรวมไลบรารีการแจ้งเตือนของ Android ไว้ใน แอปที่มาพร้อมเครื่อง โดยใช้โค้ด Wrapper ที่คุณสร้างขึ้น ขั้นตอนแรกให้อัปเดต ทรัพยากร gradle.build ระดับแอปของโปรเจ็กต์ (my_project/app/gradle.build)

อัปเดต gradle.build

  1. GNI คือไลบรารีการสนับสนุนที่จำเป็นสำหรับโค้ด Wrapper ที่สร้างขึ้น โปรเจ็กต์ทั้งหมด ที่ใช้โค้ดที่สร้างขึ้นควรอ้างอิงไลบรารีนี้ หากต้องการอ้างอิงไลบรารีนี้ เพิ่มบรรทัดต่อไปนี้ในส่วน 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. เพิ่มไลบรารี GNI ลงใน 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() องค์ประกอบ การแจ้งเตือนจะปรากฏที่ด้านบนของหน้าจออุปกรณ์ทดสอบ

สร้าง Wrapper จาก JAR

ในตัวอย่างก่อนหน้านี้ คุณได้กำหนดคลาส Java ด้วยตนเองและ เมธอดที่ต้องใช้โค้ดแบบเนทีฟในไฟล์การกำหนดค่า Wrapper สำหรับสถานการณ์ที่คุณ ต้องการเข้าถึงส่วนต่างๆ ของ API ขนาดใหญ่ จะมีประสิทธิภาพมากกว่าในการมอบ JAR ของไลบรารีเพิ่มเติมไปยังเครื่องมือ Wrapper จากนั้น Wrapper จะสร้าง Wrapper สำหรับ สัญลักษณ์สาธารณะทั้งหมดที่พบใน JAR

ตัวอย่างต่อไปนี้รวม Notifications API ทั้งหมดด้วยการระบุไลบรารี JAR

รับ JAR ที่จำเป็น

Notification API เป็นส่วนหนึ่งของแพ็กเกจ androidx.core ซึ่งมีให้บริการจาก ที่เก็บ Google Maven ดาวน์โหลดไฟล์ไลบรารี aar และแตกไฟล์ไปยัง ไดเรกทอรีที่คุณเลือก ค้นหาไฟล์ classes.jar

ไฟล์ classes.jar มีชั้นเรียนจำนวนมากนอกเหนือจากการแจ้งเตือนที่เรากำหนดไว้ ไลบรารี หากคุณให้ Wrapper ของไลบรารีเพียง classes.jar เครื่องมือ สร้างโค้ดแบบเนทีฟสำหรับทุกคลาสใน JAR ซึ่งไม่มีประสิทธิภาพ สำหรับโปรเจ็กต์ของเรา หากต้องการแก้ไขปัญหานี้ ให้ระบุไฟล์ตัวกรองไปยัง การกำหนดค่า Wrapper เพื่อจำกัดการสร้างโค้ดไว้เฉพาะการแจ้งเตือนของ JAR ใหม่

กำหนดตัวกรองการอนุญาต

กรองไฟล์เป็นไฟล์ข้อความธรรมดาที่คุณส่งไปยัง Wrapper ไลบรารี การกำหนดค่า เทมเพลตเหล่านี้ให้คุณกำหนดชั้นเรียนที่จะรวม (หรือยกเว้น) ได้ จากไฟล์ JAR ที่ให้ไว้ใน Wrapper ของไลบรารี

สร้างโปรเจ็กต์ ให้สร้างไฟล์ชื่อ allowed-symbols.txt แล้ววางลงในไฟล์ บรรทัดต่อไปนี้:

androidx.core.app.NotificationCompat*

เมื่อใช้เป็นตัวกรองการอนุญาต โค้ดที่อยู่ก่อนหน้าจะระบุเฉพาะสัญลักษณ์ ที่ชื่อขึ้นต้นด้วย androidx.core.app.NotificationCompat ระบบจะรวมชื่อดังกล่าว

เรียกใช้ Wrapper ของไลบรารี

เปิดเทอร์มินัลไปยังไดเรกทอรี JAR และเรียกใช้คำสั่งต่อไปนี้

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

คำสั่งตัวอย่างก่อนหน้านี้จะสร้างโค้ด Wrapper สำหรับคลาสที่กรอง ไปที่ไดเรกทอรี generated-jar/

การสนับสนุน

หากคุณพบปัญหาเกี่ยวกับ Wrapper ของไลบรารี โปรดแจ้งให้เราทราบ

เรียกดูข้อบกพร่อง รายงานข้อบกพร่อง
วิศวกรรม
เอกสารประกอบ