1

Hello I've created an Android class using C that does some operations. Among these procedures I want to use this one : ( just for test with Delphi )

JNIEXPORT void Java_Test_Project_Decode(JNIEnv* env, jclass clazz,jbyteArray dataIn, jbyteArray dataOut)
{
    jsize len = (*env)->GetArrayLength(env, dataIn);
    LOGV("JNI call Decode test dataIn Size = %d",len);

 jbyte *pByteIn = (*env)->GetByteArrayElements(env, dataIn, 0);
 jbyte *pByteOut = (*env)->GetByteArrayElements(env, dataOut, 0);


 *pDataOut = *pDataIn;  pDataIn++;  pDataOut++;


*pDataOut = *pDataIn;  pDataIn++;  pDataOut++;
*pDataOut = *pDataIn;  pDataIn++;  pDataOut++;

 // some routines
 (*env)->ReleaseByteArrayElements(env, dataOut, pByteOut, 0);
(*env)->ReleaseByteArrayElements(env, dataIn, pByteIn, 0);

}

of course my testlib.so is built and compiled ( NDK-build using cygwin ) and deployed with my Delphi project .

And in my Delphi Firemonkey client I use this procedure in this way :

Java_Test_Project_Decode:procedure(PEnv: PJNIEnv; Obj:JObject;dataIn:Pointer;DataOut:Pointer); cdecl;

of course after I load my library :

Procedure LoadMyLib();
begin
FMyLib := LoadLibrary(PChar(LibFolder + LibTest));
  if FMyLib = 0 then
  begin
   Exit;
  end
     else 
          begin
          Java_Test_Project_Decode:=GetProcAddress(FMyLib,'Java_Test_Project_Decode');

             if not assigned (Java_Test_Project_Decode) then
               begin
                Exit; // Java_Test_Project_Decode procedure not loaded
               end else
                     begin
                   // OK Java_Test_Project_Decode procedure loaded
                    end;
        end;
end;

Then I use the procedure :

Procedure TestMyProcedure (ADataIn: pointer; ASize: integer);
var 
ADataOut:Pointer;
begin
// ADataIn pointer is not empty 
Java_Test_Project_Decode(PEnv,Obj,ADataIn,ADataOut);
end;

But here I get an exception and the app crashes.

Please can anyone help me fixing this ?

So thanks.

Update : I can eliminate the ADataOut as follows :

JNIEXPORT void Java_Test_Project_Decode(JNIEnv* env, jclass clazz,jbyteArray dataIn)
    {
        jsize len = (*env)->GetArrayLength(env, dataIn);
        LOGV("JNI call Decode test dataIn Size = %d",len);

     jbyte *pByteIn = (*env)->GetByteArrayElements(env, dataIn, 0);

     // some routines
    (*env)->ReleaseByteArrayElements(env, dataIn, pByteIn, 0);

    }

and my Delphi declaration :

Java_Test_Project_Decode:procedure(PEnv: PJNIEnv; Obj:JObject;dataIn:Pointer); cdecl;

Procedure TestMyProcedure (ADataIn: pointer; ASize: integer);

    begin
    // ADataIn pointer is not empty 
    Java_Test_Project_Decode(PEnv,Obj,ADataIn);
    end;

But always with the same exception and error, I even tried to just get the

jsize len = (*env)->GetArrayLength(env, dataIn);
    LOGV("JNI call Decode test dataIn Size = %d",len);

same error .

blong
  • 2,145
  • 13
  • 23
randydom
  • 395
  • 2
  • 20
  • `ADataOut` in `TestMyProcedure()` is not initialized, but `Java_Test_Project_Decode()` is trying to access and write to it. `Java_Test_Project_Decode()` requires `ADataIn` and `ADataOut` to be pointers to valid **Java** byte arrays of at least 2 elements in size. – Remy Lebeau Sep 19 '16 at 17:45
  • @RemyLebeau , i can eliminate the `ADateOut` in both the Delphi and Java procedures but still getting the same exception and error . although the `ADataIn` is not empty and initialized – randydom Sep 19 '16 at 19:46
  • Please edit your question to show your updated code. – Remy Lebeau Sep 19 '16 at 20:05
  • @RemyLebeau i updated my code just to get the DataIn size only and log it , but always with the same error and exception – randydom Sep 19 '16 at 20:20
  • On Android, Delphi has a `JObject` interface that is separate from JNI's `JObject` type. Make sure you are using the JNI type in your function declaration. – Remy Lebeau Sep 19 '16 at 21:03
  • just one remark, is it not more easy to put all your .java files inside the classes.dex and call it like any other android java functions than inside a .so ? i did this and it's work like a charm ... – zeus Sep 19 '16 at 22:24
  • another remark: can you see in android device monitor in the logCat tab all the log when the app crash ? it's often very usefull ... – zeus Sep 19 '16 at 22:26
  • @loki it would appear the thing being called from Delphi is a C function exposed to the Java world via JNI and being accessed from Delphi through JNI. Since it's C it compiles to a .so file, not to a .class or .jar file, so your notion doesn't pan out. – blong Sep 19 '16 at 22:55
  • @loki , i prefer it as a .so file cause it's a cross-platform library file ( DLL inside windows and a so inside android – randydom Sep 19 '16 at 22:59
  • @randydom re Remy's last point, `JNIObject` (the type that maps to the JNI `jobject` type) is defined in the Androidapi.Jni RTL unit. `JObject` is the Delphi wrapper around `java.lang.object` and is defined in the Androidapi.JNI.JavaTypes RTL unit. You are using the latter and probably should be using the former in your Delphi declaration. – blong Sep 19 '16 at 23:02

1 Answers1

0

Your Delphi declaration needs to match the underlying C declaration, which it current does not on three counts:

  1. As Remy pointed out you are using the JObject JNI Bridge interface type (from the RTL unit Androidapi.JNI.JavaTypes.pas) that wraps up the Java class java.lang.jobject, instead of the type JNIObject (from the RTL unit Androidapi.Jni.pas), which is the equivalent of the JNI type jobject. However this is mostly irrelevant as...
  2. Your underlying C method uses a jclass parameter to imply it is a static/class method, but your Delphi import declaration is attempting to use the jobject equivalent, giving a mismatch. It should declare a parameter of type JNIClass instead.
  3. Your underlying C method uses a jbytearray JNI parameter type but your Delphi declaration is made using type Pointer. Instead you should use the Delphi equivalent to jbytearray, which is JNIByteArray.

Only when all the arguments and calling conventions on both sides match directly will the call succeed.

On an slightly related note it may have been of benefit to inform readers as to how the .so library was deployed within the Delphi project and what path expression you used to gain access to it at runtime, as this can be far from clear.

In order to perform a similar experiment I had a .so file that I set up the Deployment Manager to deploy to the assets\internal\ deployment folder. This then meant I could use TPath.Combine(TPath.GetDocumentsPath, LibName) to reference the library file from the Android app code.

blong
  • 2,145
  • 13
  • 23