JNI - the old way of doing it and the Delphi OO way

In XE5, Embarcadero introduced a new object oriented way of calling into any Android API. Before looking at it, let's look at the old way of doing it.

var
  Cls: JClass;
  Mid: JMethodID;
...
 Cls := JNIEnv.FindClass('TestClass');
 if Cls = nil then
  begin
    WriteLn('Can''t find class: TestClass');
    Exit;
  end;

  Mid := JNIEnv.GetStaticMethodID(Cls, 'printHello', '()V');
  if Mid = nil then
    begin
      WriteLn('Can''t find method: printHello');
      Exit;
    end;

  JNIEnv.CallStaticVoidMethod(Cls, Mid, []); // [] - no parameters

Imagine the tedium involved when there's plenty of methods to be called. In XE5, a class known as TJavaGenericImport takes two interfaces, one declaring all the Java methods for a class, and one declaring all the class methods, and allow them to be called directly. See my previous posting for further details.

So, if in Java, you have an instance of TestClass in tc, and you call it like this:

 tc.printHello();

in Delphi, you can do the same thing as well!

All you have to do is write two interfaces, one describing the class methods, and one describing the instance methods and then create a new class that inherits from TJavaGenericImport and specify the two interfaces as generic parameters. So, given the following Java class that was seen earlier:

public class TestClass {
	
	public TestClass() {
	}
	
	public TestClass(String msg) {
	}

	public void printHello() {
	}

	public static float[] getTestFloat() {
	}
	
	public static long[] getTestLong() {
	}

the following would be what you would need to write:

  JTestClassClass = interface(JObjectClass)
  ['{6E1DA723-41BE-4E0A-8103-B2FDED2E9307}']
    { Constructors }
    function init: JTestClass; cdecl; overload;
    function init(msg: JString): JTestClass; cdecl; overload;

    { Class Methods }
    function getTestFloat: TJavaArray<Single>; cdecl;
    function getTestLong: TJavaArray<Int64>; cdecl;
  end;

  [JavaSignature('cx/ath/journeyman/TestClass')]
  JTestClass = interface(JObject)
  ['{6D521D7D-863C-43E7-A7CF-EB57A7D3378F}']
    { Instance Methods }
    procedure printHello; cdecl;
  end;
  TJTestClass = class(TJavaGenericImport<JTestClassClass, JTestClass>) end;

The Java way:

  TestClass tc = new TestClass(); // or new TestClass("Hello");
  tc.getMinLong();

To construct an instance of TestClass, call TJTestClass.Create, or TJTestClass.JavaClass.init. Here's the Delphi way,

var
  LTestClass: JTestClass;
...
  LTestClass := TJTestClass.Create;
// or LTestClass := TJTestClass.JavaClass.init(StringToJString('Hello'));
  LTestClass.getMinLong;

The call StringToJString transforms the given Delphi string to its Java equivalent. Since XE5 came out, I've been working on writing an application that can convert any Java class to its equivalent Delphi/Android interface as there are some Android classes that were not declared, and I'm pleased to announce that the effort is now completed. To purchase the tool, visit the Android2DelphiImport tool site. To load an external JAR, see my other post.

 

Generating the import unit for Google Cloud Messaging for Delphi.

Published Thu, 7 Nov 2013 @ 10:45 AM by chuacw
Related articles: ,

Comments (Will be reviewed before being published)

# Android2DelphiImport tool

Thursday, January 30, 2014 11:57 AM by Jim McKeeth

I've tested this out for importing Android APIs and it works fabulously! What a great resource!

Leave a Comment

(required) 
(required) 
(optional)
(required) 
Enter the following code to ensure that your comment reaches the intended party:
Enter the numbers you see in the image: