Wrapper ของไลบรารีสำหรับ API ของ Android เป็นส่วนหนึ่งของชุดเครื่องมือพัฒนาเกม Android
Wrapper ของไลบรารีคือเครื่องมือบรรทัดคำสั่ง (CLI) ที่สร้างภาษา C โค้ด Wrapper สำหรับ API ของ Android ที่เขียนด้วย Java คุณใช้ โค้ดในแอป Android ที่มาพร้อมเครื่องเพื่อเรียกใช้ Java API ได้โดยไม่จำเป็นต้องสร้างโค้ด สร้าง Java Native Interface หรือ JNI เครื่องมือนี้สามารถทำให้การพัฒนาง่ายขึ้น แอป Android ที่เขียนด้วยภาษา C หรือ C++ เป็นหลัก
เครื่องมือจะทำงานโดยสร้างโค้ด C สำหรับสัญลักษณ์สาธารณะที่อยู่ใน ไฟล์ Java Archive (JAR) ที่คุณระบุ หรือคลาสที่กำหนดไว้ในไฟล์ หรือทั้ง 2 ไฟล์ก็ได้ โค้ดที่เครื่องมือสร้างขึ้นไม่ได้แทนที่ Java API; แต่จะทำหน้าที่เป็นสะพานเชื่อมระหว่างโค้ด C และ Java แอปของคุณยังอยู่ ต้องการไลบรารี Java ที่คุณรวมไว้ในโปรเจ็กต์ของคุณ
ดาวน์โหลด
ดาวน์โหลดที่เก็บถาวร Wrapper ของไลบรารีและแตกเนื้อหาไปยังไดเรกทอรี ตามที่คุณต้องการ
วากยสัมพันธ์
เครื่องมือ Wrapper ไลบรารีมีไวยากรณ์ของบรรทัดคำสั่งต่อไปนี้
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 Wrapper สามารถระบุ JAR ได้หลายรายการ
ตัวอย่างเช่น: วันที่ -i first_library.jar -i second_library.jar...
|
-o output-path |
ตำแหน่งระบบไฟล์สำหรับโค้ดที่สร้างขึ้น |
-c config-file |
เส้นทางระบบไฟล์ไปยังไฟล์การกำหนดค่า Wrapper ของไลบรารี โปรดดูรายละเอียด ดูส่วนการกำหนดค่า |
-fa allow-list-file |
เส้นทางไปยังไฟล์ตัวกรองที่คุณสามารถระบุสัญลักษณ์สำหรับเครื่องมือเพื่อ รวม โปรดดูรายละเอียดในส่วนตัวกรอง |
-fb block-list-file |
เส้นทางไปยังไฟล์ตัวกรองที่มีสัญลักษณ์ซึ่งไม่รวมอยู่ในการตัดข้อความ สำหรับ รายละเอียด โปรดดูที่ส่วนตัวกรอง |
--skip_deprecated_symbols |
สั่งให้เครื่องมือ Wrapper ข้าม @เลิกใช้งานแล้ว สัญลักษณ์ |
ไฟล์การกำหนดค่า Wrapper
การกำหนดค่า Wrapper ไลบรารีคือไฟล์ 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
พารามิเตอร์ พารามิเตอร์ทั้ง 2 รายการใช้พร้อมกันได้ หากตัวกรองทั้งสอง
ระบบจะรวมสัญลักษณ์ไว้เมื่อมีการกำหนดไว้ในไฟล์ตัวกรองอนุญาต
และไม่อยู่ในไฟล์ตัวกรองการบล็อก
สถานการณ์ตัวอย่าง
สมมติว่าคุณต้องตัดไฟล์ JAR ChatLibrary.jar
ที่มีส่วน
ชั้นเรียนต่อไปนี้:
public class ChatManager {
public static void sendMessage(int userId, String message) {...}
}
โปรเจ็กต์ C ต้องการให้คุณสร้าง Wrapper แบบเนทีฟสำหรับ JAR นี้ แอป Android ที่มาพร้อมเครื่องเพื่อเรียกใช้ในระหว่างรันไทม์ สร้างโค้ดนี้โดยใช้ Wrapper ของไลบรารีที่มีคำสั่งต่อไปนี้
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);
ดูตัวอย่างสถานการณ์แบบเจาะลึกได้ในคำแนะนำเกี่ยวกับ Wrapper ของไลบรารี
รายละเอียดเครื่องมือ
ส่วนต่อไปนี้จะให้ข้อมูลโดยละเอียดเกี่ยวกับ Wrapper ของไลบรารี
โครงสร้างไดเรกทอรีเอาต์พุต
ไฟล์ซอร์สและส่วนหัว C ทั้งหมดอยู่ในไดเรกทอรีย่อยที่แสดง
ชื่อแพ็กเกจของคลาส Java ที่รวมเอาไว้ด้วย ตัวอย่างเช่น โค้ด Wrapper สำหรับพร็อพเพอร์ตี้
JAR java.lang.Integer
ที่ระบุสร้างขึ้นในไดเรกทอรี
./java/lang/integer.[h/cc]
คุณสามารถควบคุมลักษณะการทํางานของเอาต์พุตนี้ได้โดยใช้เครื่องมือ configuration
วงจรของออบเจ็กต์
ออบเจ็กต์ Java จะแสดงเป็นตัวชี้แบบทึบในโค้ด C ซึ่งเรียกว่า Wrapper Wrapper จะจัดการการอ้างอิง JNI สำหรับออบเจ็กต์ Java ที่เกี่ยวข้อง Wrapper ของกระป๋อง สร้างขึ้นในสถานการณ์ต่อไปนี้
- รวมการอ้างอิง JNI ที่มีอยู่ด้วยการเรียกฟังก์ชัน
MyClass_wrapJniReference(jobject jobj)
ฟังก์ชัน ไม่ได้รับสิทธิ์การเป็นเจ้าของข้อมูลอ้างอิงที่ให้ไว้ แต่สร้างข้อมูลอ้างอิงของตนเอง การอ้างอิง JNI ทั่วโลก - ด้วยการสร้างออบเจ็กต์ใหม่ ซึ่งเทียบเท่ากับการเรียกใช้ตัวสร้างใน
Java:
MyClass_construct()
- ด้วยการส่งคืน Wrapper ใหม่จากฟังก์ชัน เช่น
Score* Leaderboard_getScore(Leaderboard* instance, String* leaderboard_name)
คุณต้องทำลาย Wrapper ทั้งหมดเมื่อไม่ได้ใช้งานแล้ว ในการดำเนินการนี้ โทรหาผู้ดูแลระบบ
destroy()
ฟังก์ชัน MyClass_destroy(MyClass* instance)
ฟังก์ชันที่ส่งกลับ Wrapper จะจัดสรรหน่วยความจำใหม่ให้สำหรับการเรียกทุกครั้ง แม้ว่า Wrapper จะเป็นอินสแตนซ์ Java เดียวกัน
ตัวอย่างเช่น เมื่อ Method ของ Java Singleton.getInstance()
แสดงผลเมธอด
อินสแตนซ์เดียวกัน ฟังก์ชันเทียบเท่าในฝั่ง C จะสร้างอินสแตนซ์ใหม่
ของ Wrapper สำหรับอินสแตนซ์ 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 ที่ระบุ Wrapper ของคลังจะสร้าง การใช้งานขั้นพื้นฐานซึ่งประกอบด้วยตัวชี้ที่คลุมเครือและวิธีการต่อไปนี้
wrapJniReference()
getJniReference()
destroy()
รายละเอียดการสร้างโค้ด
เมื่อเรียกใช้ Wrapper ของไลบรารีจะสร้างโค้ด C ตามสัญลักษณ์สาธารณะใน ไฟล์ JAR ที่คุณให้เครื่องมือ โค้ด C ที่สร้างขึ้นอาจแสดงความแตกต่าง จากโค้ด Java ที่รวมเอาไว้ เช่น C ไม่รองรับฟีเจอร์อย่าง OOP ประเภททั่วไป, เมธอดโอเวอร์โหลด หรือฟีเจอร์ Java อื่นๆ
โค้ด C ที่สร้างขึ้นที่แสดงถึงสถานการณ์เหล่านี้อาจแตกต่างจากประเภทของ ที่นักพัฒนาซอฟต์แวร์ C คาดหวัง ตัวอย่างในส่วนต่อไปนี้ บริบทเกี่ยวกับวิธีที่เครื่องมืออาจสร้าง C จากโค้ด Java หมายเหตุ: ในข้อมูลโค้ด ตัวอย่างต่อไปนี้รวมถึงโค้ด C/C++ และ Java ข้อมูลเพิ่มเติม ข้อมูลโค้ดเหล่านี้มีวัตถุประสงค์เพื่อแสดงวิธีที่เครื่องมือ จะสร้างโค้ดสำหรับแต่ละสถานการณ์ที่กำหนด
ชั้นเรียน
คลาสจะแสดงเป็นตัวชี้แบบทึบใน C ดังนี้
C/C++
typedef struct MyClass_ MyClass;
Java
public class MyClass { ... }
อินสแตนซ์ของตัวชี้ทึบแสงจะเรียกว่า Wrapper เครื่องมือ Wrapper
จะสร้างฟังก์ชันการสนับสนุนเพิ่มเติมสำหรับแต่ละคลาส สำหรับตัวอย่างก่อนหน้านี้
คลาส 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) { ... }
}
}
ประเภททั่วไป
Wrapper ของไลบรารีไม่ได้รวมประเภททั่วไปโดยตรง แต่เครื่องมือดังกล่าว สร้าง Wrapper สำหรับการสร้างอินสแตนซ์ประเภททั่วไปเท่านั้น
ตัวอย่างเช่น เมื่อมีคลาส MyGeneric<T>
อยู่ใน API และมี 2
อินสแตนซ์ของคลาสนี้ เช่น MyGeneric<Integer>
และ
MyGeneric<String>
ระบบจะสร้าง Wrapper สำหรับ 2 อินสแตนซ์นั้น ช่วงเวลานี้
หมายความว่าคุณไม่สามารถสร้างอินสแตนซ์ใหม่ของประเภท 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()
และระบุ
ฟังก์ชัน Callback สำหรับอินเทอร์เฟซแต่ละวิธี ติดตั้งใช้งานได้เฉพาะอินเทอร์เฟซเท่านั้น
ในลักษณะนี้ ระบบไม่รองรับชั้นเรียนและคลาสนามธรรม โปรดดู
ตัวอย่างต่อไปนี้
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);
ข้อจำกัด
เครื่องมือ Wrapper ไลบรารีอยู่ในเวอร์ชันเบต้า คุณอาจประสบกับสิ่งต่อไปนี้ ข้อจำกัด:
โครงสร้าง Java ที่ไม่รองรับ
Wrapper ของไลบรารีรุ่นเบต้าไม่รองรับการสร้างต่อไปนี้
เมธอดโอเวอร์โหลด
ภาษา C ไม่อนุญาตให้ประกาศฟังก์ชัน 2 รายการที่มีชื่อเดียวกัน ถ้า คลาสใช้วิธีโอเวอร์โหลด โค้ด C ที่สร้างขึ้นจะไม่คอมไพล์ วิธีแก้ปัญหาคือการใช้เพียงวิธีเดียวที่มีชุดพารามิเตอร์ที่เพียงพอ คุณกรองฟังก์ชันที่เหลือออกได้โดยใช้ตัวกรอง ช่วงเวลานี้ ยังใช้ได้กับผู้ก่อสร้างด้วย
เมธอดที่ใช้เทมเพลต
ช่องที่ไม่ใช่
static final int
และstatic final String
อาร์เรย์
ชื่อที่อาจขัดแย้งกัน
เนื่องจากคลาสของ Java แสดงในโค้ด C จึงอาจมีชื่อ
ในบางกรณีซึ่งพบได้ไม่บ่อยนัก เช่น คลาส Foo<Bar>
และคลาสภายใน
คลาส Bar
ในคลาส Foo
จะแสดงด้วยสัญลักษณ์เดียวกันใน C ดังนี้
typedef struct Foo_Bar_ Foo_Bar;
การสนับสนุน
หากคุณพบปัญหาเกี่ยวกับ Wrapper ของไลบรารี โปรดแจ้งให้เราทราบ
เรียกดูข้อบกพร่อง | รายงานข้อบกพร่อง |
---|---|
วิศวกรรม | bug_report |
เอกสารประกอบ | bug_report |