自作のJavaクラスからDelphiの関数を呼び出すには¶
Androidアプリケーションで自作のJavaクラスからDelphiの関数を呼び出す方法を説明します。
次のようなサンプルアプリケーションを作成します。
addボタンを押すと、JavaのHelloクラスのaddメソッドが呼ばれます。 addメソッドは、DelphiのhelloOnAdd関数を実行します。 DelphiのhelloOnAdd関数は引数の2つの値を加算した結果を返します。
sayボタンを押すと、JavaのHelloクラスのsayメソッドが呼ばれます。 sayメソッドは、DelphiのhelloOnSay関数を実行します。 DelphiのhelloOnAdd関数は引数の文字列を大文字に変換します。
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;
アプリケーションを実行する¶
アプリケーションを実行します。