Android API のライブラリ ラッパー   Android Game Development Kit の一部。

ライブラリ ラッパーは、Java で記述された Android API の C 言語ラッパーコードを生成するコマンドライン ツール(CLI)です。ネイティブ Android アプリでこのコードを使用すると、Java ネイティブ インターフェース(JNI)を手動で作成しなくても、Java API を呼び出すことができます。このツールを使用すると、主に C または C++ で作成される Android アプリの開発を簡素化できます。

このツールは、指定した Java Archive(JAR)ファイル、またはツールの構成ファイルで定義されたクラスのパブリック シンボル、あるいはそれらの両方に対して C コードを生成することで機能します。ツールによって生成されたコードは Java API に代わるものではありません。その代わりに、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 C ラッパーコードを生成する JAR ファイル。複数の 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 @Deprecated のシンボルをスキップするようラッパーツールに指示します。

ラッパー構成ファイル

ライブラリのラッパー構成は、コード生成プロセスを制御できるようにする 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/

上記のコマンドにより、ディレクトリ ./generated_code に C ソースコードが生成されます。生成されたファイル 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] に生成されます。

この出力動作は、ツールの構成ファイルを使用して制御できます。

オブジェクトのライフサイクル

Java オブジェクトは、ラッパーと呼ばれる不透明ポインタとして C コードで表現されます。ラッパーは、対応する Java オブジェクトの JNI 参照を管理します。ラッパーは次のシナリオで作成できます。

  • 関数 MyClass_wrapJniReference(jobject jobj) を呼び出して、既存の JNI 参照をラップします。この関数は、指定された参照の所有権を取得しませんが、独自のグローバル JNI 参照を作成します。
  • 新しいオブジェクトを作成する。これは Java のコンストラクタ 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()

コード生成の詳細

実行時に、ツールに対して指定した JAR ファイル内のパブリック シンボルに基づいてライブラリ ラッパーが C コードを生成します。生成された C コードは、ラップされた Java コードとは異なる場合があります。たとえば、C は OOP、汎用型、メソッドのオーバーロード、その他の Java 機能などの機能をサポートしていません。

これらの状況を反映する生成された C コードは、C デベロッパーが想定する種類のコードとは異なる場合があります。以降のセクションの例は、ツールが Java コードから C を生成する方法に関するコンテキストを示しています。注: コード スニペットの次の例には、C/C++ と Java のコード スニペットが含まれます。これらのスニペットは、ツールが所定の状況ごとにコードを生成する方法を示すことのみを目的としています。

クラス

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> が API に存在し、MyGeneric<Integer>MyGeneric<String> など、このクラスの 2 つのインスタンス化が存在する場合は、これら 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();
}

インターフェースを実装する

implementInterface() を呼び出して各インターフェース メソッドにコールバック関数を指定することで、C インターフェースを実装します。この方法で実装できるのはインターフェースのみです。クラスと抽象クラスはサポートされていません。次の例をご覧ください。

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 言語では、同じ名前を持つ 2 つの関数を宣言することはできません。クラスでメソッドのオーバーロードを使用すると、生成された C コードはコンパイルされません。回避策は、十分なパラメータ セットを持つメソッドを 1 つのみ使用することです。残りの関数は、フィルタを使用して除外できます。これはコンストラクタについても同様です。

  • テンプレート化されたメソッド

  • static final intstatic final String 以外のフィールド

  • 配列

名前の競合の可能性

Java クラスが C コードで表される際の方法により、ごくまれに名前の競合が発生する場合があります。たとえば、Foo クラス内のクラス Foo<Bar> と内部クラス Bar は、C では同じシンボル typedef struct Foo_Bar_ Foo_Bar; で表されます。

サポート

ライブラリのラッパーに問題がある場合は、お知らせください。

バグを参照する バグを報告する
エンジニアリング
ドキュメント