This document describes how to access a JAVA Class that you have created with ECLIPSE/ANDROID STUDIO or access a third party package.

If you want to access ANDROID SDK JAVA classes that are not already exposed in the Embarcadero XE5 September 2013 release. e.g. BLUETOOTH. see ANDROID in XE5

The September release of Embarcadero XE5 adds ANDROID support to the Delphi/Firemonkey framework. XE5 compiles to native ARM7/NEON code - In fact, the native code resides within the NATIVE ACTIVITY CLASS running on the virtual machine. The Java Native Interface (JNI) is a programming framework that enables Java code running in a Java Virtual Machine (JVM) to call and be called by other programming languages. Using this mechanism you can directly access a java class from Delphi XE5 but in the absence of example projects, it is a difficult proccess - read a new XE5 user experience


What follows is a "worked example" supporting the article


Use ANDROID STUDIO to create an example JAVA CLASS


Locate the application package file (APK) created by this compilation

Open the file with WINZIP

Extract the file CLASSES.DEX


Locate the utility DEXDUMP.EXE

Where to find DEXDUMP

// If you have Android Studio installed
C:\Users\{name}\AppData\Local\Android\android-studio\sdk\build-tools\18.0.1
C:\Users\{name}\AppData\Local\Android\android-studio\sdk\build-tools\android-4.2.2

OR

{ANDROIDPATH}\build-tools\android-4.2.2 
i.e. co-located with Aapt location - see TOOLS - OPTIONS - Enviroment options - SDK manager

ANDROIDPATH is in key HKEY_CURRENT_USER\Software\Embarcadero\BDS\12.0
C:\Users\Public\Documents\RAD Studio\12.0\PlatformSDKs\adt-bundle-windows-x86-20130522\sdk\build-tools\android-4.2.2

{ANDROIDPATH}\build-tools\android-4.2.2 
i.e. DEXDUMP co-located with Aapt.exe - see TOOLS - OPTIONS - Enviroment options - SDK manager


ANDROIDPATH is in REGEDIT key HKEY_CURRENT_USER\Software\Embarcadero\BDS\12.0


Extract class information using DEXDUMP

C:\Users\Pete\ClassTestingProject\ClassTesting\build\apk>dexdump classes.dex>dexdump.txt
C:\Users\Pete\ClassTestingProject\ClassTesting\build\apk>notepad dexdump.txt

Find each method in our class and note the signatures

Method name Signature Meaning
storedintvalue I Integer property
getIntValue ()I Method has no parameters - returns an Integer
setIntValue (I)V Method has a single integer parameter - void return
stunt (Ljava/lang/String;I)V Method has two parameters String, Integer - void return


Drag and drop JAVA APK to the target device

We use the download directory.


Use the Android Debug bridge utility to check the file location

You can find ADB.EXE in the platform-tools directory

e.g.
C:\Users\Public\Documents\RAD Studio\12.0\PlatformSDKs\adt-bundle-windows-x86-20130522\sdk\platform-tools

ClassTesting-debug-unaligned.apk will be loaded by the Delphi application from the directory /storage/sdcard0/download

The -d switch selects the first physical connection (not the emulator)

The shell command engages an interface that will accept linux like commands

The cd command navigates to the target directory

The ls command lists the contents of the target directory

C:\Users\Pete\android\adb>adb -d shell
shell@grouper:/ $ cd /storage/sdcard0/download
cd /storage/sdcard0/download
shell@grouper:/storage/sdcard0/download $ ls
ls
ClassTesting-debug-unaligned.apk
TabbedApplication-3.apk
install_flash_player_ics.apk
tabbedapplication-1.apk
tabbedapplication-2.apk
tabbedapplication.apk
shell@grouper:/storage/sdcard0/download $ ^C
C:\Users\Pete\android\adb>

Create a Firemonkey mobile application

The project needs two buttons and a memo window

unit Ubridge2far;

// Demo JNI in XE5
// (c) RedTitan technology 2013

interface

uses
  System.SysUtils,
  System.Classes,
  System.Variants,
  FMX.Types,
  FMX.Graphics,
  FMX.Controls,
  FMX.Forms,
  FMX.Dialogs,
  FMX.TabControl,
  FMX.StdCtrls,
  FMX.Objects,
  FMX.Layouts,
  FMX.Memo,
  FMX.Edit,
  FMX.ListBox,
  FMX.ExtCtrls,
  FMX.Platform,
  TypInfo,
  Androidapi.JNIBridge,
  Androidapi.JNI.JavaTypes,
  Androidapi.Jni,
  Androidapi.JNI.Dalvik;

type
  TForm1 = class(TForm)
    Memo1: TMemo;
    Panel1: TPanel;
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    data:string;
    JavaEnv:PJNIEnv;
    JavaObject:JObject;
    JavaObjectID:JNIObject;
    GetMethod:JNIMethodID;
    SetMethod:JNIMethodID;
    StuntMethod:JNIMethodID;
    StrData: JNIString;
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}
uses
  Androidapi.JNI.GraphicsContentViewText,
  FMX.Helpers.Android;

procedure TForm1.Button1Click(Sender: TObject);
const
// test_apk_fn='/storage/emulated/0/Download/ClassTesting-debug-unaligned.apk';
 test_apk_fn='/storage/sdcard0/Download/ClassTesting-debug-unaligned.apk';

 javaClassName='com/example/classtesting/RTClassWrapTest1';

var
  CL:JDexClassLoader;
  jTempClass:Jlang_Class;
  jTemp:JObject;
  oTemp:TObject;
  jLocalInterface:ILocalObject;
  context:JContext;
  optimizedpath_jfile:JFile;
  dexpath_jstring,optimizedpath_jstring:JString;
  ji:JNIInt;
  jv:JNIValue;
  x:array of JNIOBJECT;
begin

  context:=SharedActivityContext;
  JavaEnv:=TJNIResolver.GetJNIEnv;


  memo1.lines.Add('Loading external library from "'+test_apk_fn+'"');
  dexpath_jstring:=StringToJString(test_apk_fn);

  // locate/create a directory where our dex files can be put
  optimizedpath_jfile:=context.getDir(StringToJString('outdex'),TJContext.javaclass.mode_private);


  optimizedpath_jstring:=optimizedpath_jfile.getAbsolutePath;

  memo1.lines.Add('Path for DEX files = '+JStringToString(optimizedpath_jstring));
  memo1.lines.Add('APK containing target class = '+JStringToString(dexpath_jstring));

  cl:=TJDexClassLoader.JavaClass.init
    (dexpath_jstring,optimizedpath_jstring,nil,TJDexClassLoader.JavaClass.getSystemClassLoader);

  if not assigned(cl) then
  begin
    memo1.Lines.Add('?Failed to get DEXClassLoader');
    exit;
  end;

  jTempClass:=cl.loadClass(StringToJString(javaClassName));

  if assigned(jTempClass) then
  begin
    jTemp:=jTempClass;    // N.B You could now import the entire class


    if jTemp.QueryInterface(ILocalObject,jLocalInterface)=S_OK then
    begin
      // supports ilocalobject
      JavaObject:=jTempClass.newInstance;
      oTemp:=JavaObject as TObject;
      JavaObjectID:=tjavaimport(otemp).GetObjectID;
      memo1.Lines.Add(oTemp.ClassName);

      GetMethod:=TJNIResolver.GetJavaMethodID((jTempClass as ILocalObject).GetObjectID,'getIntValue','()I');
      if not assigned(Getmethod) then
      begin
        memo1.Lines.Add('?getIntvalue not supported');
        exit;
      end;

      ji:=MaxInt;
      ji:=JavaEnv^.CallIntMethod(JavaEnv,JavaObjectID,GetMethod);
      memo1.Lines.Add('getIntValue returns '+inttostr(ji));

      SetMethod:=TJNIResolver.GetJavaMethodID((jTempClass as ILocalObject).GetObjectID,'setIntValue','(I)V');
      if not assigned(Setmethod) then
      begin
        memo1.Lines.Add('?setIntvalue not supported');
        exit;
      end;

      ji:=1234;
      jv.i:=ji;
      JavaEnv^.CallVoidMethodA(JavaEnv,JavaObjectID,SetMethod,@jv);

      ji:=JavaEnv^.CallIntMethod(JavaEnv,JavaObjectID,GetMethod);
      memo1.Lines.Add('External method returns '+inttostr(ji));

      StuntMethod:=TJNIResolver.GetJavaMethodID(
        (jTempClass as ILocalObject).GetObjectID,'stunt','(Ljava/lang/String;I)V');
      if not assigned(stuntMethod) then
      begin
        memo1.Lines.Add('?stunt not supported');
        exit;
      end;
      // there are two parameters
      setlength(x,2);

      // first parameter is a string
      x[0]:=StringToJNIString(JavaEnv,'Parameter to be displayed in the console window');

      // second parameter is an integer
      jv.i:=2333;
      x[1]:=jv.l;
      JavaEnv^.CallVoidMethodV(JavaEnv,JavaObjectID,stuntMethod,x); // (Ljava/lang/String;I)V



    end else
    begin
      memo1.Lines.Add('?Could not derive ILOCALOBJECT');
      exit;
    end;

  end else memo1.Lines.Add('?'+javaClassname+' not found')
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  memo1.lines.Clear;
end;

end.


Monitor the output in the ADB console window

It should look something like this

C:\Users\Pete\android\adb>adb -d logcat
D/classtesting( 7726): ****************getIntValue called *************
D/classtesting( 7726): ****************setIntValue called *************
D/classtesting( 7726): ****************getIntValue called *************
D/classtesting( 7726): ****************stunt called *************
D/classtesting( 7726): Parameter to be displayed in the console window 2333
D/classtesting( 7726): Parameter to be displayed in the console window 2333
I/ActivityManager(  432): Start proc com.android.providers.calendar for content
provider com.android.providers.calendar/.CalendarProvider2: pid=17081 uid=10007
gids={50007, 3003, 1015, 1028}
.
.
.
.

Nexus 7 screenshot

Tablet should look something like this

nexus 7
© RedTitan Technology 2013