randydom
randydom

Reputation: 405

Delphi consume a procedure from an Android Jni wrapper

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 .

Upvotes: 1

Views: 590

Answers (1)

blong
blong

Reputation: 2175

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.

Upvotes: 0

Related Questions