自作のJavaクラスからDelphiの関数を呼び出すには

Androidアプリケーションで自作のJavaクラスからDelphiの関数を呼び出す方法を説明します。

None

次のようなサンプルアプリケーションを作成します。

_images/101.gif

addボタンを押すと、JavaのHelloクラスのaddメソッドが呼ばれます。 addメソッドは、DelphiのhelloOnAdd関数を実行します。 DelphiのhelloOnAdd関数は引数の2つの値を加算した結果を返します。

_images/102.gif

sayボタンを押すと、JavaのHelloクラスのsayメソッドが呼ばれます。 sayメソッドは、DelphiのhelloOnSay関数を実行します。 DelphiのhelloOnAdd関数は引数の文字列を大文字に変換します。

_images/103.gif

Javaクラスを作成する

Delphiの関数を宣言する

Javaのクラスでは、Delphiの関数にはnative修飾子をつけて宣言します。

// 2つの数値を引数にとり、数値を返す関数
public native int helloOnAdd(int A, int B);

// 文字列を引数にとり、引数を返す関数
public native String helloOnSay(String Word);

Delphiの関数を読み出すJavaクラスを作成する

C:\tmp\src\com\example\hello\Hello.javaに、Delphiの関数を呼び出すJavaクラスを作成します。

package com.example.hello;

public class Hello {

  // 2つの数値を引数にとり数値を返すDelphiの関数
  public native int helloOnAdd(int A, int B);

  // 文字列を引数にとり、引数を返す関数
  public native String helloOnSay(String Word);

  // AddメソッドはDelphiの関数に処理を委譲する
  public int Add(int A, int B) {
      return helloOnAdd(A, B);
  }

  // sayHelloメソッドはDelphiの関数に処理を委譲する
  public String sayHello() {
      return helloOnSay("Hello");
  }
}

HelloクラスのAddメソッドは、Delphiの関数helloOnAdd関数を呼び出します。

HelloクラスのsayHelloメソッドは、Delphiの関数helloOnSay関数を呼び出します。

jarファイルを作成する

C:\tmp\src\com\example\hello\Hello.javaをコンパイルし、classファイルをC:\tmp\bin\classes\com\example\hello\に作成します。

> cd c:\tmp\
>
> mkdir bin\classes
>
> javac -d bin\classes src\com\example\hello\Hello.java

C:\tmp\bin\classesにあるcomフォルダーからjarファイルを作成します。

> jar cvf bin\Hello.jar -C bin\classes com

ブリッジファイルを作成する

JavaのHelloクラスのラッパークラスを作成します。

Java2OP.exe -jar bin\Hello.jar -unit Android.JNI.Hello

Android.JNI.Hello.pasが作成されました。

{*******************************************************}
{                                                       }
{           CodeGear Delphi Runtime Library             }
{ Copyright(c) 2014 Embarcadero Technologies, Inc.      }
{                                                       }
{*******************************************************}

unit Android.JNI.Hello;

interface

uses
  Androidapi.JNIBridge,
  Androidapi.JNI.JavaTypes;

type
// ===== Forward declarations =====

  JHello = interface;//com.example.hello.Hello

// ===== Interface declarations =====

  JHelloClass = interface(JObjectClass)
    ['{9400A21A-8E99-4540-BB15-4BC9DD556E3D}']
    {class} function init: JHello; cdecl;
  end;

  [JavaSignature('com/example/hello/Hello')]
  JHello = interface(JObject)
    ['{36CC45EE-7DD5-46BA-93FB-14A43E13B5BA}']
    function add(P1: Integer; P2: Integer): Integer; cdecl;
    function helloOnAdd(P1: Integer; P2: Integer): Integer; cdecl;
    function helloOnSay(P1: JString): JString; cdecl;
    function say(P1: JString): JString; cdecl;
  end;
  TJHello = class(TJavaGenericImport<JHelloClass, JHello>) end;

implementation

procedure RegisterTypes;
begin
  TRegTypes.RegisterType('Android.JNI.Hello.JHello', TypeInfo(Android.JNI.Hello.JHello));
end;

initialization
  RegisterTypes;
end.

DelphiでJavaから呼び出される関数を作成する

関数を作成する

JavaのHelloクラスから呼び出されるHelloOnAdd関数を作成します。

Helloクラスでは、helloOnAdd関数は次のように定義していました。

// 2つの数値を引数にとり数値を返すDelphiの関数
public native int helloOnAdd(int A, int B);

Javaから呼び出されるDelphiの関数には、呼び出し規約「cdecl」をつけます。

引数は、第一引数と第二引数はPJNIEnvとJNIObjectに決まっています。 三番目以降の引数は、関数を呼び出すJavaから渡される引数になります。

// 引数の整数を足した結果を返す
function HelloOnAdd(PEnv: PJNIEnv; This: JNIObject; A: Integer; B: Integer): Integer; cdecl;
begin
  Result := A + B;
end;

JavaのHelloクラスから呼び出されるHelloOnSay関数を作成します。

Helloクラスでは、helloOnSay関数は次のように定義していました。

// 文字列を引数にとり、引数を返す関数
public native String helloOnSay(String Word);

Javaの文字列の受け渡しには、JNIString型を使用します。

// 引数の文字列を大文字にして返す
function HelloOnSay(PEnv: PJNIEnv; This: JNIObject; Word: JNIString): JNIString; cdecl;
var
  S: String;
begin
  S := JNIStringToString(PEnv, Word);
  S := UpperCase(S);
  Result := StringToJNIString(PEnv, S);
end;

Delphiの関数とJavaのクラスを関連づける

JNIEnvのRegisterNativesメソッドで、Delphiの関数とJavaのクラスを関連づけます。

例:関連づける関数が一つの場合

uses
  Androidapi.Jni,
  Androidapi.JNIBridge;

procedure RegisterDelphiNativeMethods;
var
  PEnv: PJNIEnv;
  Clazz: JNIClass;
  NativeMethod: JNINativeMethod;
begin
  try
    PEnv := TJNIResolver.GetJNIEnv;
    Clazz := TJNIResolver.GetJavaClassID('com.example.hello.Hello');
    if not assigned(Clazz) then
    begin
      Log.d('Clazz is nil.');
      Exit;
    end;

    NativeMethod.Name := 'helloOnAdd';
    NativeMethod.Signature := '(II)I';
    NativeMethod.FnPtr := @Unit1.HelloOnAdd;
    PEnv^.RegisterNatives(PEnv, Clazz, @NativeMethod, 1);
    PEnv^.DeleteLocalRef(PEnv, Clazz);
  except
    on E: Exception do
      Log.d(E.Message);
  end;
end;

例:関連づける関数が二つ以上の場合

uses
  Androidapi.Jni,
  Androidapi.JNIBridge;

procedure RegisterDelphiNativeMethods;
var
  PEnv: PJNIEnv;
  Clazz: JNIClass;
  NativeMethods: array[0..1] of JNINativeMethod;
begin
  try
    PEnv := TJNIResolver.GetJNIEnv;
    Clazz := TJNIResolver.GetJavaClassID('com.example.hello.Hello');
    if not assigned(Clazz) then
    begin
      Log.d('Clazz is nil.');
      Exit;
    end;

    NativeMethods[0].Name := 'helloOnAdd';
    NativeMethods[0].Signature := '(II)I';
    NativeMethods[0].FnPtr := @Unit1.HelloOnAdd;
    NativeMethods[1].Name := 'helloOnSay';
    NativeMethods[1].Signature := '(Ljava/lang/String;)Ljava/lang/String;';
    NativeMethods[1].FnPtr := @Unit1.HelloOnSay;
    PEnv^.RegisterNatives(PEnv, Clazz, @NativeMethods[0], 2);
    PEnv^.DeleteLocalRef(PEnv, Clazz);
  except
    on E: Exception do
      Log.d(E.Message);
  end;
end;

initializationセクションにRegisterDelphiNativeMethodsを登録し、アプリケーションを実行したときに実行するようにします。

initialization
  RegisterDelphiNativeMethods;
end.

これで、JavaのクラスからDelphiの関数が呼び出されるようになりました。

ここで行っている処理を簡単に説明すると、TJNIResolver.GetJavaClassIDメソッドでJavaクラスを取得します。

Clazz := TJNIResolver.GetJavaClassID('com.example.hello.Hello');

JNINativeMethod型の配列に、関連づけるDelphiの関数を設定します。

NativeMethods[0].Name := 'helloOnAdd';
NativeMethods[0].Signature := '(II)I';
NativeMethods[0].FnPtr := @Unit1.HelloOnAdd;

SignatureはJavaのメソッドの型を表す文字列です。 書式は「(引数)戻り値」となります。

型は次の文字を使います。

  • Z:JNIBoolean
  • B:JNIByte
  • C:JNIChar
  • S:JNIShort
  • I:JNIInt
  • J:JNILong
  • F:JNIFloat
  • D:JNIDouble
  • L:JNIObject

たとえば「(II)I」は「数値型を2つ受け取り、戻り値に数値型を返す」という意味になります。

RegisterNativesメソッドでJavaクラスとDelphiの関数を関連づけます。 RegisterNativesメソッドの4番目の引数はNativeMethods配列の要素数です。

PEnv^.RegisterNatives(PEnv, Clazz, @NativeMethods[0], 2);

アプリケーションを実行する

アプリケーションを実行して関数が呼び出されることを確認します。

FireMonkeyモバイルアプリケーションを作成し、フォームにボタンとラベルを配置します。

Javaクラスのメソッドを実行する

DelphiからJavaクラスのメソッドを実行します。

ボタンを押したときのイベントを追加します。

uses
  Androidapi.Jni, //PJNIEnv JNIObject JNIString JNIStringToString StringToJNIString
  Androidapi.Helpers, //JStringToString
  Android.Jni.Hello;

procedure TForm1.Button1Click(Sender: TObject);
var
  Hello: JHello;
begin
  Hello := TJHello.Create;
  Label1.Text := IntTOStr(Hello.add(1, 2));
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  Hello: JHello;
begin
  Hello := TJHello.Create;
  Label1.Text := JStringToString(Hello.say(Stringtojstring('hello world')));
end;

アプリケーションを実行する

アプリケーションを実行します。

_images/101.gif _images/102.gif _images/103.gif