Wrapper libreria per le API Android Parte del Android Game Development Kit.
Il wrapper libreria è uno strumento a riga di comando (CLI) che genera per le API Android scritte in Java. Puoi utilizzare questo il codice nelle app Android native per chiamare le API Java senza dover scrivere manualmente Creare una interfaccia nativa Java o JNI. Questo strumento semplifica lo sviluppo App per Android scritte principalmente in C o C++.
Lo strumento genera codice C per i simboli pubblici contenuti nei file JAR (Java Archive) forniti da te o nelle classi definite nel di configurazione del deployment o entrambi. Il codice generato dallo strumento non sostituisce Java le API funge invece da ponte tra il codice C e Java. La tua app è ancora richiede che le librerie Java sottoposte a wrapping siano incluse nel progetto.
Scarica
Scarica l'archivio del wrapper libreria e aprine il contenuto nella directory di tua scelta.
Sintassi
Lo strumento per il wrapper libreria ha la seguente sintassi della riga di comando:
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]
Parametro | Descrizione |
---|---|
-i jar-file-to-be-wrapped |
JAR per generare il codice wrapper C. È possibile specificare più JAR,
ad esempio: -i first_library.jar -i second_library.jar...
|
-o output-path |
Posizione del file system per il codice generato. |
-c config-file |
Percorso del file system del file di configurazione del wrapper libreria. Per maggiori dettagli, consulta la sezione sulla configurazione. |
-fa allow-list-file |
Il percorso di un file filtro in cui puoi specificare i simboli dello strumento per a capo. Per maggiori dettagli, consulta la sezione Filtro. |
-fb block-list-file |
Il percorso di un file filtro contenente simboli esclusi dal wrapping. Per consulta la sezione Filtro. |
--skip_deprecated_symbols |
Indica allo strumento wrapper di saltare @Obsoleto . |
File di configurazione del wrapper
La configurazione del wrapper libreria è un file JSON che ti consente di controllare il processo di generazione del codice. Il file utilizza la seguente struttura.
{
// 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.
]
},
]
}
Filtra file
Può essere utile escludere alcuni simboli dai file JAR che intendi eseguire il wrapping. Tu puoi specificare nella configurazione un file filtro per escludere i simboli. Un file filtro è un semplice file di testo in cui ogni riga definisce un simbolo da mandare a capo. Filtra l'utilizzo dei file la seguente sintassi:
java-symbol-name java-jni-type-signature
Di seguito è riportato un esempio di file di filtro:
# 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
Nella configurazione devi fornire un file di filtro specificando i simboli che
sono consentiti utilizzando il parametro -fa
e i simboli bloccati utilizzando il -fb
. Entrambi i parametri possono essere utilizzati contemporaneamente. Se vengono visualizzati entrambi i filtri
fornito, un simbolo verrà aggregato quando viene definito nel file del filtro di autorizzazione
e non è presente nel file del filtro a blocchi.
Scenario di esempio
Supponi di dover eseguire il wrapping del file JAR ChatLibrary.jar
contenente
seguente:
public class ChatManager {
public static void sendMessage(int userId, String message) {...}
}
Il tuo progetto C richiede la generazione di un wrapper nativo per questo JAR, abilitando dall'app Android nativa per chiamarla durante il runtime. Genera questo codice utilizzando un wrapper libreria con il seguente comando:
java -jar lw.jar -i ChatLibrary.jar -o ./generated_code/
Il comando precedente genera il codice sorgente C nella directory
./generated_code
. Il file generato chat_manager.h
contiene un codice simile a
di seguito, per consentirti di chiamare la libreria nel tuo progetto:
#include "java/lang/string.h"
typedef struct ChatManager_ ChatManager;
void ChatManager_sendMessage(int32_t user_id, String* message);
Per uno scenario di esempio dettagliato, consulta la Guida ai wrapper libreria.
Dettagli dello strumento
Le seguenti sezioni forniscono informazioni dettagliate sul wrapper della libreria funzionalità.
Struttura della directory di output
Tutti i file di origine e di intestazione C si trovano in sottodirectory che riflettono
nome del pacchetto della classe Java sottoposta a wrapping. Ad esempio, il codice wrapper per il
viene generato il JAR java.lang.Integer
specificato nella directory
./java/lang/integer.[h/cc]
.
Puoi controllare questo comportamento dell'output utilizzando il metodo di configurazione.
Ciclo di vita degli oggetti
Gli oggetti Java sono rappresentati nel codice C come puntatori opachi, chiamati wrapper. Un wrapper gestisce un riferimento JNI per un oggetto Java corrispondente. Un wrapper può nei seguenti scenari:
- Aggregando un riferimento JNI esistente richiamando la funzione
MyClass_wrapJniReference(jobject jobj)
. La funzione non acquisisce la proprietà del riferimento fornito, ma crea un proprio riferimento globale di JNI. - Creando un nuovo oggetto, che equivale a chiamare un costruttore
Java:
MyClass_construct()
- Restituendo un nuovo wrapper da una funzione, ad esempio:
Score* Leaderboard_getScore(Leaderboard* instance, String* leaderboard_name)
Devi eliminare tutti i wrapper quando non sono più utilizzati. A questo scopo, chiama il team
Funzione destroy()
MyClass_destroy(MyClass* instance)
.
Le funzioni che restituiscono i wrapper allocano una nuova memoria per ogni chiamata. anche se i wrapper rappresentano la stessa istanza Java.
Ad esempio, quando il metodo Java Singleton.getInstance()
restituisce sempre
stessa istanza, la funzione equivalente sul lato C creerà una nuova istanza
di un wrapper per la stessa istanza 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.
Gestire le classi senza riferimenti
Quando non è possibile trovare una classe in un JAR fornito, il wrapper bibliotecario crea un implementazione di base composta da un puntatore opaco e i seguenti metodi:
wrapJniReference()
getJniReference()
destroy()
Dettagli sulla generazione del codice
Quando viene eseguito, il wrapper della libreria genera codice C in base ai simboli pubblici in i file JAR forniti dallo strumento. Il codice C generato potrebbe presentare differenze dal codice Java aggregato. Ad esempio, C non supporta funzionalità quali OOP, tipi generici, sovraccarico di metodi o altre funzionalità Java.
Il codice C generato che riflette queste situazioni può essere diverso dal tipo previsto dagli sviluppatori C. Gli esempi nelle sezioni seguenti forniscono contesto su come lo strumento può generare C dal codice Java. Nota: negli snippet di codice, i seguenti esempi includono C/C++ e codice Java gli snippet di codice. Questi snippet hanno il solo scopo di dimostrare in che modo lo strumento genera il codice per ogni situazione.
Classi
Le classi sono rappresentate come puntatori opachi in C:
C/C++
typedef struct MyClass_ MyClass;
Java
public class MyClass { ... }
Le istanze di puntatori opachi sono indicate come wrapper. Lo strumento wrapper
genera funzioni di supporto aggiuntive per ogni corso. Per l'esempio precedente
classe MyClass
, vengono generate le seguenti funzioni:
// 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);
Costruttori
Le classi con costruttori pubblici o predefiniti sono rappresentate utilizzando :
C/C++
MyClass* MyClass_construct(String* data);
Java
public class MyClass {
public MyClass(String data) { ... }
}
Metodi
I metodi sono rappresentati come funzioni normali. Il nome di una funzione contiene
nome originale del corso. Le funzioni che rappresentano metodi di istanza non statici hanno
primo parametro un puntatore a una struttura che rappresenta un oggetto Java,
per conto del quale viene chiamata la funzione. Questo approccio è analogo all'this
il puntatore del mouse.
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) { ... }
}
Corsi interni
Le classi interne sono rappresentate da vicino alle classi normali, ad eccezione del nome della classe la struttura C corrispondente contiene i nomi concatenati delle classi esterne:
C/C++
typedef struct MyClass_InnerClass_ MyClass_InnerClass;
Java
public class MyClass {
public class InnerClass {...}
}
Metodi della classe interna
I metodi delle classi interne sono rappresentati come segue:
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) { ... }
}
}
Tipi generici
Il wrapper libreria non aggrega direttamente i tipi generici. Lo strumento genera wrapper solo per le istanze di tipo generico.
Ad esempio, quando in un'API esiste una classe MyGeneric<T>
e sono presenti due
delle istanze di questa classe, ad esempio MyGeneric<Integer>
e
MyGeneric<String>
, vengono generati i wrapper per queste due istanze. Questo
significa che non puoi creare nuove istanze di tipo MyGeneric<T>
utilizzando
configurazioni di tipo diverso. Vedi l'esempio che segue:
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();
}
Implementa le interfacce
Implementa un'interfaccia C chiamando implementInterface()
e fornendo un
funzione di callback per ogni metodo di interfaccia. È possibile implementare solo le interfacce
in questo modo; e le classi astratte non sono supportate. Consulta le
nell'esempio seguente:
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);
}
Esempio di utilizzo:
void onAction1() {
// Handle action 1
}
void onAction2(int32_t data) {
// Handle action 2
}
Observer* observer = Observer_implementInterface(onAction1, onAction2);
Subject_registerObserver(subject, observer);
Limitazioni
Lo strumento wrapper libreria è in versione beta. Potresti riscontrare quanto segue: limitazioni:
Costrutti Java non supportati
La versione beta del wrapper libreria non supporta i seguenti costrutti:
Sovraccarico del metodo
Il linguaggio C non consente di dichiarare due funzioni con lo stesso nome. Se utilizza il sovraccarico del metodo, il codice C generato non si compilerà. La alternativa consiste nell'utilizzare un solo metodo con un set sufficiente di parametri. Le altre funzioni possono essere filtrate con i filtri. Questo si applica anche ai costruttori.
Metodi basati su modelli
Campi diversi da
static final int
estatic final String
Array
Potenziali conflitti di nomi
A causa del modo in cui le classi Java sono rappresentate nel codice C, potrebbero essere
in casi molto rari. Ad esempio, una classe Foo<Bar>
e una
classe Bar
all'interno di una classe Foo
sono rappresentate dallo stesso simbolo in C:
typedef struct Foo_Bar_ Foo_Bar;
Assistenza
Se riscontri un problema con il wrapper della biblioteca, faccelo sapere.
Sfoglia i bug | Segnala un bug |
---|---|
Ingegneria | bug_report |
Documentazione | bug_report |