About the author
The new Delphi XE5 provides support for the Android platform, and provides an object oriented JNI framework that allows one to access Android/Java classes (in the rest of this article, I'll refer to it as Java instead, as it's shorter) by merely redeclaring them as a Delphi class inheriting from the TJavaGenericImport class implementing two interfaces, like the following example:
JDialogInterface_OnKeyListenerClass = interface(IJavaClass) ['{F95ED60A-27E5-4A1C-8081-E4646C4AF61D}'] end; [JavaSignature('android/content/DialogInterface$OnKeyListener')] JDialogInterface_OnKeyListener = interface(IJavaInstance) ['{597D3989-B3F3-4AE2-8521-1D1141FBE3E3}'] {Methods} function onKey(dialog: JDialogInterface; keyCode: Integer; event: JKeyEvent): Boolean; cdecl; end; TJDialogInterface_OnKeyListener = class(TJavaGenericImport< JDialogInterface_OnKeyListenerClass, JDialogInterface_OnKeyListener>) end;
The first interface allows direct access to public static Java methods, or in Delphi parlance, class methods. The second interface allows access to instance methods. To instantiate the above Java class, one calls either
dlgIntf := TJDialogInterface_OnKeyListener.Create(...);
or
dlgIntf := TJDialogInterface_OnKeyListener.JavaClass.init(...);
and then, to access any instance methods (for the above class), just access it from dlgIntf, like so:
dlgIntf.onKey(...)
Since Java is implemented in C/C++, the calling convention for Java/Android methods is cdecl.
The TJavaGenericImport class provides access to the declared Java class by building a method table for the Java class, with each method pointing to a stub built during runtime that does some parameter wrapping, before using JNI to call into the actual Java method.
I decided to port the Android ARM JNI framework to Win32, after I was approached to write a wrapper. Another motivation was that debugging any Android code on Delphi XE5 didn't get very far. The IDE would hang often when debugging any code on Android. The first target platform for the Android JNI port is Win32, instead of Win32 and Win64, because parameter passing on Win64 is very different from Win32.
The initial prototype was written in Java. After the prototype was completed, I decided to dogfood the prototype by rewriting it in Delphi, hence, providing myself the opportunity to test out the generated wrappers.
The port involved converting ARM assembly to x86 assembly, as well as the rest of the Android JNI framework. Other than that, it also involved creating a Java VM in Delphi, since the generated wrappers require a Java VM in place, and calling parts of the Java framework from within Delphi, using the same generated wrappers as well.
During the port, I noticed that the Delphi ARM JNI framework may possibly not handle return results such as float, double, or any kind of Java array. So I quickly learnt some assembly that took float/double results, and convert it into a form more palatable to Delphi. This also meant that my application might generate Android/Java class wrappers that exposes bugs in the Delphi JNI RTL.
The Android 2 Delphi Import application has been ready for weeks. I'm now figuring out what my best options are:
In XE5, Embarcadero introduced a new object oriented way of calling into any Android API. Before looking
Hello,
I found your article very interesting.
Anderson, if you need consulting, let me know. Just leave me a comment with your email, and I'll follow up.
Continued discussion of undocumented Delphi 8 Property Access Specifiers, and other ways of adding and removing delegates / events handlers, including clearing the list of all the delegates / event handlers.
This article discusses the new Delphi 8 property access specifiers.
A method pointer is now the same as a global procedure, ie, procedure of object = procedure.