Access to Java classes in Delphi

Delphi uses JNIBridge to access to Java classes using JNI. Delphi represents all external Java classes by interfaces. One interface is needed for Java class methods and another for the instance methods.

For example, here is a declaration of Java Boolean class located in Androidapi.JNI.JavaTypes.pas:

JBooleanClass = interface(JObjectClass)

['{CD51CE90-BCDA-4291-99B0-7BC70033C3CB}']

{class} function _GetFALSE: JBoolean; cdecl;

{class} function _GetTRUE: JBoolean; cdecl;

{class} function _GetTYPE: Jlang_Class; cdecl;

{class} function init(string_: JString): JBoolean; cdecl; overload;

{class} function init(value: Boolean): JBoolean; cdecl; overload;

{class} function compare(lhs: Boolean; rhs: Boolean): Integer; cdecl;

{class} function getBoolean(string_: JString): Boolean; cdecl;

{class} function parseBoolean(s: JString): Boolean; cdecl;

{class} function toString(value: Boolean): JString; cdecl; overload;

{class} function valueOf(string_: JString): JBoolean; cdecl; overload;

{class} function valueOf(b: Boolean): JBoolean; cdecl; overload;

{class} property FALSE: JBoolean read _GetFALSE;

{class} property TRUE: JBoolean read _GetTRUE;

{class} property &TYPE: Jlang_Class read _GetTYPE;

end;

[JavaSignature('java/lang/Boolean')]

JBoolean = interface(JObject)

['{21EAFAED-5848-48C2-9998-141B57439F6F}']

function booleanValue: Boolean; cdecl;

function compareTo(that: JBoolean): Integer; cdecl;

function equals(o: JObject): Boolean; cdecl;

function hashCode: Integer; cdecl;

function toString: JString; cdecl; overload;

end;

TJBoolean = class(TJavaGenericImport) end;

TJBoolean is a helper class that allows to get access to Java Boolean class or instance.

Accesing to the class provided by JavaClass method. When you create an instance of Java class, you can access all instance method declared in JBoolean.

To create new instance of Java class you should use Java-class constructor called init.

var

BoolValue: JBoolean; // Java Interface

begin

// Instantiate using constructor

BoolValue :=TJBoolean.JavaClass.init(True);

end;

Memory management

All Java instances managed by Java runtime, Delphi JNI bridge hold global link to java instances, which tell Java runtime do not destroy instance. When Delphi interfaces is destroyed, Java instances destroyed by garbage collection.

Write custom Java class using TJavaObject

Custom Java classes are needed when you need to subclass an existing Java class.

The base class for it is TJavaObject, another way is use TJavaGeneric class, which provide direct access to super class members declared in JavaInterface.

To declare custom Java class you should declare new interface new class inherited from TJavaObject (or TJavaGeneric) class.

Here is an example of subclassing Android View class:

[JavaSignature('com/turbococoa/customview/CustomView')]

JCustomView = interface(JView)

['{69D7FFB8-F3BB-4976-8D6E-1E966A3887C3}']

procedure onDraw(const canvas: JCanvas);

end;

TCustomView = class(TJavaGeneric)

private

protected

public

procedure onDraw(const canvas: JCanvas);

end;

...

procedure TCustomView.onDraw(const canvas: JCanvas);

begin

… custom drawing

end;

initialization

TCustomView.RegisterJava;

First you need declare custom attribute [JavaSignature] to Java interface.

In some cases Java runtime create an instance automatically (for example Activity or layout members) using class name and signature. To allow this for custom Java classes it should be registered by calling RegisterJava method in initialization section.

To create a new instance of custom class, just call the constructor:

var

Obj: TCustomView;

...

Obj := TCustomView.Create;

...

The instance of custom class destroyed by garbage collection, when Delphi instance is destroyed.

To access to super class members use TJavaObject.Super property.

If you need to post reference to your custom java class instance to external API, you can use the following two methods:

1. If API requires Delphi interface, you should use TJavaObject.Super property

2. If API requires JNI object, you should use TJavaObject.JNIObject property.

Implement virtual method in custom class

To write virtual method in custom class, you should declare it in custom class interface and mark it using special attribute [Override]. For example:

[JavaSignature('com/turbococoa/controls/SeekBarActivity')]

JSeekBarActivity = interface(JActivity)

['{2688244E-D2EE-4AE3-8779-CE500F4B4224}']

[Override]

procedure onCreate(const savedInstanceState: JBundle);

...

If you need to call inherited Java class method in your implementation, use TJavaObject.CallSuper method. TJavaObject.CallSuper makes sense only in virtual method marked by [Override] attribute.

procedure TSeekBarActivity.onCreate(const savedInstanceState: JBundle);

begin

CallSuper; // This method call super class onCreate

...

end;

Use actions

The easiest way to implement JView_onClickListener is just implement action in your custom class and set action name in layout XML file.

To make an action, just declare and implement special method in your custom class and mark it by [IBAction] attribute:

...

[IBAction]

procedure ButtonClick(id: Pointer);

...

The set onClick property in layout XML file in Android Studio designer or just in XML file like this:

...

android:onClick="ButtonClick" />

Write Java listeners

Android classes usually uses listeners to callback from java instances. Let's describe how to use listeners on example of handling onClick event for Button.

You should write JView_OnClickListener implementation for this.

TMyButtonClickListener = class(TJavaLocal, JView_OnClickListener)

private

public

{ JView_OnClickListener }

procedure onClick(v: JView); cdecl;

end;

….

procedure TMyButtonClickListener.onClick(v: JView);

begin

… // Action

end;

To set listener to particular Button instance, you should create listener instance and set it to the Button. For example in Activity.onCreate:

procedure TMyActivity.onCreate(const savedInstanceState: JBundle);

var

L: TMyButtonClickListener;

begin

CallSuper;

Super.setContentView(R.layout.mainactivity);

L := TMyButtonClickListener.Create;

Super.findViewById(R.id.seekBar).setOnClickListener(L);

end;

Implementing Java listener in custom class

Another way to handle actions is implementing Java listener directly in your custom class, for example in Activity class. To do that, you should add listener meebers to custom class interface and implement it in Delphi class. Here code from Controls demo, SeekBarActivity1.pas:

[JavaSignature('com/turbococoa/controls/SeekBarActivity')]

JSeekBarActivity = interface(JActivity)

['{2688244E-D2EE-4AE3-8779-CE500F4B4224}']

...

{ JSeekBar_OnSeekBarChangeListener }

procedure onProgressChanged(seekBar: JSeekBar; progress: Integer; fromUser: Boolean); cdecl;

procedure onStartTrackingTouch(seekBar: JSeekBar); cdecl;

procedure onStopTrackingTouch(seekBar: JSeekBar); cdecl;

end;

TSeekBarActivity = class(TJavaGeneric, JSeekBar_OnSeekBarChangeListener)

...

{ JSeekBar_OnSeekBarChangeListener }

procedure onProgressChanged(seekBar: JSeekBar; progress: Integer; fromUser: Boolean); cdecl;

procedure onStartTrackingTouch(seekBar: JSeekBar); cdecl;

procedure onStopTrackingTouch(seekBar: JSeekBar); cdecl;

end;

procedure TSeekBarActivity.onCreate(const savedInstanceState: JBundle);

begin

CallSuper;

Super.setContentView(R.layout.seekbaractivity1);

FSeekBar := TJSeekBar.Wrap(Super.findViewById(R.id.seekBar));

FSeekBar.setOnSeekBarChangeListener(Self); // set listener

end;

{ JSeekBar_OnSeekBarChangeListener Implementation }

procedure TSeekBarActivity.onProgressChanged(seekBar: JSeekBar; progress: Integer; fromUser: Boolean);

begin

… // Action

end;

procedure TSeekBarActivity.onStartTrackingTouch(seekBar: JSeekBar);

begin

end;

procedure TSeekBarActivity.onStopTrackingTouch(seekBar: JSeekBar);

begin

end;

FAQ Docs Videos Samples About Us