将JIntArray转换为Object of Array

时间:2014-02-06 15:53:40

标签: arrays delphi java-native-interface

function Java_com_erm_controller_ARMReports_S35(PEnv: PJNIEnv; Obj: JObject; ex_UserRowID, ex_BSID : Integer; ex_RevalDate : JString;
               ex_AFS, ex_HTM, ex_HFT : Boolean;
               ex_IsMcCaulay_PNL: Boolean;
               ex_Maturity, ex_Scale : JIntArray 
               ): Integer; stdcall; export;
var objRpt : TARMReports;
I : Integer;
Len : JInt; //just a renamed delphi integer
aMaturity:array of Integer;
aScale:array of Integer;

begin
   DLLErrorLog('CASH -S35');
  objRpt := TARMReports.Create; JVM := TJNIEnv.Create(PEnv); ex_RevalDate_J := JVM.JStringToString(ex_RevalDate);

  Len:=PEnv^.GetArrayLength(PEnv, ex_Maturity);
  SetLength(aMaturity, Len);
  Len:=PEnv^.GetArrayLength(PEnv, ex_Scale);
  SetLength(aScale, Len);

  DLLErrorLog('ex_Maturity Length'+ intToStr(Len));

  for I := 0 to Len-1 do
    begin
      PEnv^.GetIntArrayRegion(PEnv, ex_Maturity, I, Len, @aMaturity[I]);
      DLLErrorLog('ex_Maturity '+ IntToStr(aMaturity[I]));

      PEnv^.GetIntArrayRegion(PEnv, ex_Scale, I, Len, @aScale[I]);
      DLLErrorLog('ex_Scale '+ IntToStr(aScale[I]));
    end;


 Result := objRpt.S35(ex_UserRowID, ex_BSID,  ex_RevalDate_J,
               ex_AFS, ex_HTM, ex_HFT ,
               ex_IsMcCaulay_PNL,
               aMaturity, aScale
               );

   DLLErrorLog('CASH2 Ends -S35');

  JVM.Free; objRpt.Free;
end;

需要将ex_Maturityex_Scale转换为Delphi的Array of Integer对象。

现在从Java调用它会抛出 java.lang.ArrayIndexOutOfBoundsException

在Log数组值中打印时正在进行。请建议我们为我工作。

1 个答案:

答案 0 :(得分:2)

有几种方法,具体取决于您的JIntArray

首先,如果它是 int 的数组(如原始java类型),则通过JNI获取数组的长度,分配一个整数的delphi数组,然后让JNI复制来自java数组的数据

Uses
  AndroidAPI.JNI;
Var
  Len:JNIInt; //just a renamed delphi integer
  aMaturity:array of integer;
begin
  Len:=PEnv^.GetArrayLength(PEnv, ex_Maturity);

  //allocate the receiving array
  SetLength(aMaturity, Len);

  //now get the array data - note we are passing the address of the first element 
  //not the address of the array itself!
  PEnv^.GetIntArrayRegion(PEnv, ex_Maturity, 0, Len, @aMaturity[0]);

  //do stuff
end;

如果您正在处理一个Integer数组(即Java类“Integer”),那么您需要一次从JNI获取一个对象数组,并使用TJNIResolver获取原始值;

Uses
  AndroidAPI.JNI, AndroidAPI.JNIBridge;
Var
  Len:JNIInt; //just a renamed delphi integer
  Count:Integer;
  Current:JNIObject;
  CurrentValue:integer;
  aMaturity:array of integer;
begin
  Len:=PEnv^.GetArrayLength(PEnv, ex_Maturity);

  //allocate the receiving array
  SetLength(aMaturity, Len);

  For Count:=0 to Len-1 do
    begin
      Current:=PEnv^.GetObjectArrayElement(PEnv, ex_Maturity, Count);

      if assigned(Current) then
        begin
          CurrentValue:=TJNIResolver.GetRawValueFromJInteger(Current);
          //Yes, you can inline this but the point is, here you do stuff with 
          //the element
          aMaturity[Count]:=CurrentValue;
        end;
    end;
end;

显然第一种方法要快得多,因为越过JNI障碍并且你只做了一次,而对于Java Integers数组你每次做多次元件

您还应该注意错误 - 如果您不处理它们,我不会在任何可能崩溃和烧毁您应用的点检查Java异常。

编辑:OP已准备好我的答案并尝试使用它,这很好。他们的代码中有一个超出范围的例外。

function Java_com_erm_controller_ARMReports_S35(PEnv: PJNIEnv; Obj: JObject; ex_UserRowID, ex_BSID : Integer; ex_RevalDate : JString;
           ex_AFS, ex_HTM, ex_HFT : Boolean;
           ex_IsMcCaulay_PNL: Boolean;
           ex_Maturity, ex_Scale : JIntArray 
           ): Integer; stdcall; export;
var objRpt : TARMReports;
    I : Integer;
    Len : JInt; //just a renamed delphi integer
    aMaturity:array of Integer;
    aScale:array of Integer;
begin
   DLLErrorLog('CASH -S35');
  objRpt := TARMReports.Create; JVM := TJNIEnv.Create(PEnv); ex_RevalDate_J := JVM.JStringToString(ex_RevalDate);

  //you only have 1 length defined and possibly different array lengths
  //process arrays seperately
  Len:=PEnv^.GetArrayLength(PEnv, ex_Maturity);
  SetLength(aMaturity, Len);
  DLLErrorLog('ex_Maturity Length'+ intToStr(Len));

  //only call this once, also watch the parameters you are passing in 
  PEnv^.GetIntArrayRegion(PEnv, ex_Maturity, 0, Len, @aMaturity[0]);


  Len:=PEnv^.GetArrayLength(PEnv, ex_Scale);
  SetLength(aScale, Len);

  DLLErrorLog('ex_Scale Length'+ intToStr(Len));
  PEnv^.GetIntArrayRegion(PEnv, ex_Scale, 0, Len, @aScale[0]);


  Result := objRpt.S35(ex_UserRowID, ex_BSID,  ex_RevalDate_J,
                 ex_AFS, ex_HTM, ex_HFT ,
                 ex_IsMcCaulay_PNL,
                 aMaturity, aScale
                 );

  DLLErrorLog('CASH2 Ends -S35');

  JVM.Free; objRpt.Free;
end;

你正在做的是获得两次长度,正确设置delphi数组,然后在同一个循环中循环它们,而不考虑它们可能是不同的长度。你对 getinarrayregion 的调用也为两个调用的第二个参数传递了aScale的完整长度 - 如果你真的想让每个调用都像那样循环那么你需要传递计数和一个长度为1只返回1个元素 - 这很可能是造成异常的原因。

如果你想报告内容,然后创建一个程序来做,而不是在你当前的程序中使用循环,你将不得不复制和粘贴循环来做它否则这是,不明确的坏编码实践和我们我现在不想要吗?

讽刺 并不是期望有人试图帮助你纠正你的代码,而不是真正理解问题是更好的,但哼哼。 讽刺