Welcome to Singapore blogs

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

Chee Wee's discoveries and tech blog

Changing the world one line of code at a time...
Generic Content

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

  • Comments 1

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.

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • Post
  • I've tested this out for importing Android APIs and it works fabulously! What a great resource!

Page 1 of 1 (1 items)
Blog - Monthly Archive List
Archives