2Pascal-新时代的Pascal

标题: JNI 翻译 转 Delphi 的 经验 方法 [打印本页]

作者: [北京]老猫    时间: 2014-10-10 00:00
标题: JNI 翻译 转 Delphi 的 经验 方法
请注意,本文非常古老了,JNI 定义,建议用工具自动完成。
本文只适用于 手动写 JNI 定义。

另外,可能存在 错误,懒得改了。

注意:如果您看了本文,翻译了 JNI,请发布到本群共享一份。
不同意本规定的,请立即删除本文。
凡是看了的,就表示您同意本规定。


请大家从群共享下载(① FireMonkey[DELPHI XE5]  165232328)
某某某 源代码 带调用非官方 JNI 示例.zip
里头的
Androidapi.JNI.TelephonyGemini.pas
就是我自己转的接口。

或者
重启你的手机 源代码 V1.1
里头也有一些转好的接口。


转换前需要先找到对应的 JAVA 定义文件。最好是 *.java 的。

而且要记住路径(相对于 sources\版本 的路径,下面的例子中自己参详)。
如果是第三方的 JAR 加入到 DELPHI  的 DEX 后 他也是有路径的。他的 JAVA 文件中的包名就是个路径。

一个接口对象分四部分完成。


type

  //第一部分,预定义接口对象。后面的注释大家最好都加上。
  {Class forward declarations}
  JPhoneNumberFormattingTextWatcherEx = interface;//com.mediatek.telephony.PhoneNumberFormattingTextWatcherEX
  JTelephonyManagerMTKGemini = interface;//com.mediatek.telephony.TelephonyManagerEx
  JSmsManagerGeminiMTK = interface;//android.telephony.GeminiSmsManager


  //第二部分,定义接口的类成员(类方法和属性)的对象。
JPhoneNumberFormattingTextWatcherExClass = interface(JObjectClass)
['{0A3E7ECF-F81E-4062-9487-032C3E209466}']
  {Methods}
end;

  //第三部分,定义接口的普通成员的对象。
[JavaSignature('com/mediatek/telephony/PhoneNumberFormattingTextWatcherEx')]
JPhoneNumberFormattingTextWatcherEx = interface(JObject)
['{752F5521-E746-449E-A9A8-7AE73FF63B05}']
  {Methods}
end;
  //第四部分,定义接口的构造器。
TJPhoneNumberFormattingTextWatcherEx = class(TJavaGenericImport<JPhoneNumberFormattingTextWatcherExClass, JPhoneNumberFormattingTextWatcherEx>) end;



其中
[JavaSignature('com/mediatek/telephony/PhoneNumberFormattingTextWatcherEx')]
[JavaSignature('android/telephony/gemini/GeminiSmsManager')]
这个必须注意,路径千万不能错。路径不包括扩展名,也不包括 sources\版本 目录。

但是类对象在 DELPHI 中的名称可以自己决定,当然一致最好。

然后要注意,接口中 GUID 请一定要用一个新的。用 CTRL+SHIFT+G 即可产生。


打开你的 XXX.java 文件(一般在 PlatformSDKs\adt-bundle-windows-x86-20130522\sdk\sources\版本 下面)。

查看下他的类型。如果是普通的类 class,那么我们称之为 JObject
如果是 interface ,那么我们称之为 IJavaInstance 。

在定义他们的 第二 和 第三 部分的时候,他们的 父类(基类) 就要做相应的调整。

如果 XXX 为 JObject
  //第二部分,定义接口的类成员(类方法和属性)的对象。
JXXXClass = interface(JObjectClass)
  //第三部分,定义接口的普通成员的对象。
[JavaSignature('com/mediatek/telephony/XXX')]
JXXX = interface(JObject)
注意大小写。注意 XXX 前头加了个 J。这是 EMB 的惯例。


如果 IYYY 为 IJavaInstance
  //第二部分,定义接口的类成员(类方法和属性)的对象。
JIYYYClass = interface(IJavaClass)
  //第三部分,定义接口的普通成员的对象。
[JavaSignature('com/mediatek/telephony/IYYY')]
JIYYY = interface(IJavaInstance)
注意大小写。注意 IYYY 前头加了个 J。这是 EMB 的惯例。
IYYY 前头的 I 是 java 的惯例。


这样大体的结构就有了。

接下来我们为 类成员建立具体的内容(第二部分)。
第一步 我们要从 *.java 文件中找出 public static 的 成员。有 普通的变量常量,也有函数。
如果是变量常量,就需要在下面的 2.1 部分写出读写的方法。大部分是只读的。读写函数必须是 _getXXXX 和 _setXXXX ,大小写要注意。XXXX 是被读写的属性。
这些属性放在 2.3 部分。
如果是函数则应该在 2.2 部分定义。

然后查找下有没有个叫 getDefault 的 类方法,这个一般是建议使用的构造函数。
其他没有名称的构造函数,都要翻译成  init 。而且可以重载。一般就一个。
如果就一个还是私有的,那么就没有默认构造函数,就不能写 init。

然后找剩下的 public static 的 函数。这些要翻译到 2.2 部分。

这里要感谢 全知全能的 [成都]白马王子(286258698) ,他发现了新的经验。

如果是 非 static 的 public 的变量常量,需要在下面的 3.1 部分写出读写的方法。这些属性放在 3.3 部分。


JXXXClass = interface(JObjectClass)
['{782DB705-BAF3-4F7B-98B2-DA3D6E304292}']
  {Property Methods}
  //2.1 这里是内置的静态属性读写定义。
  function _getXXXX: Integer;
  function _getYYYY: JString;
  {Methods}
  //2.2 这里是构造函数和类方法。
  function init: JXXX; cdecl;
  {Properties}
  //2.3 这里是静态属性。
  property XXXX: Integer read _getXXXX;
  property YYYY: JString read _getYYYY;
end;
也就是说 JXXXClass 是 XXX 对象的构造、属性还有类方法的定义处。


剩下的 public 但不是 static 的 函数 也就是 非静态的方法,要定义到第三部分。

[JavaSignature('com/mediatek/telephony/XXX')]
JXXX = interface(JObject)
['{D70F5051-D98A-46B4-AC6F-D5874EC7612C}']
  //3.1 这里是内置的非静态的属性读写定义。
  function _getDDDD: Integer;
  procedure _SetDDDD(Value: Integer);
  function _getEEEE: JString;
  {Methods}
  //3.2 这里是非静态的方法。
  function theYYYY: Integer; cdecl;
  {Properties}
  //3.3 这里是非静态的属性。
  property DDDD: Integer read _getDDDD write _setDDDD;
  property EEEE: JString read _getEEEE;
end;

注意方法都是 cdecl 调用规则,如果遇到同名的请重载。_get _set 开头的也就是属性的读写函数不能写调用规则。



接下来说明下基本的类型变化。

Int 就是 Integer ,很多基本对象大家都可以自己想到。
string 是 JString 。
Uri 要翻译成 Jnet_Uri。

int [] 就是 TJavaArray<Integer> ,基本类型用 TJavaArray<>。
但是 string [] 是 TJavaObjectArray<JString>,对象类型的一般都用 TJavaObjectArray<>。

ArrayList<String> 要翻译成 JArrayList
而且任何 ArrayList<PendingIntent> 也就是 ArrayList<某对象> 都要翻译成 JArrayList。

有些类型的名称比较特别。例如 java 的 Phone 类型 EMB 已经翻译成了 JCommonDataKinds_Phone。这是因为 Phone 是 CommonDataKinds 的内部类。

如果发现不认识的类型,那么就应该是别的文件定义的。
这时候就需要把这个“别的文件”也翻译过来,这样才能顺利的完成翻译。

例如

Build.java 中 Build 类,你完成他的翻译后,就可以使用 JBuild(按规则来的名称) 了。 当然这个类 EMB 已经翻译过了。



虽然 *.java 是分属在不同的文件中的,但是我们可以把一组相关的类都定义在一个 Unit 中。 大家可以参考 EMB 的做法。

如果一个 java 文件内有多个类对象,例如 ContactsContract.java 那么就应该按如下的方法写。
JContactsContract_Contacts = interface;//android.provider.ContactsContract$Contacts
类内部的类要如下写法。
JStreamItems_StreamItemPhotos = interface;//android.provider.ContactsContract$StreamItems$StreamItemPhotos

[JavaSignature( 后面的部分和上面的一样。
大家可以参考 Androidapi.JNI.Provider 的写法。

【好消息】:java2op 或者 JarOrClass2pas 都能自动翻译接口了。大家不用人工做了。



接口的对象的使用。

一般如果你打算使用类方法和属性,可以不建立对象。
使用 TJXXX.JavaClass.YYYY 就可以了。
如果是 创建对象,有 getDefault 就一定要用。没有就 init 。

一般建立的方法如下:
XJ对象 := YJ对象.某函数(返回 XJ对象)
或者
XJ对象 := TJ对象类.javaclass.init 或其他构造函数。

特殊J服务对象,需要用 SharedActivityContext.getSystemService(TJContext.JavaClass.某服务); 的方式建立。
例如:
procedure TMainForm.Button1Click(Sender: TObject);
var
  TM: JTelephonyManager;
var
  TelephonyServiceNative: JObject;
begin
  TelephonyServiceNative := SharedActivityContext.getSystemService(TJContext.JavaClass.TELEPHONY_SERVICE);
  if Assigned(TelephonyServiceNative) then
    TM := TJTelephonyManager.Wrap((TelephonyServiceNative as ILocalObject).GetObjectID);
  try
    Edit1.Text := JStringToString(TM.getLine1Number);
  except
    on E: Exception do
    begin
      ShowMessage('发生错误' + sLineBreak + E.Message);
    end;
  end;
end;





如果你使用一个叫 JXXXX 的接口,但是运行发生错误提示,没有找到他。一般是 [JavaSignature( 这个地方的路径错误,或者说,这个机器上就没有这个接口。

如果你使用一个方法,发生非法操作,说明没有这个方法(大概是名称或参数有错误)。

如果你使用一个方法死机了,说明这个方法定义到了错误的位置。


如何实现一个 回调(事件),请参考下面的代码。
TAlertDialogOnClickListener
TCopyButtonClickListener
TVKListener


如何继承一个 接口,请参考下面的代码。(当然这个,最好别分成两个类,下面的代码是特殊需求。其实一个类就行。最好是参考 如何实现一个 回调(事件))
TCommonAlertDialogRunner
TInputQueryDialogRunner


在使用 回调或者事件 或者 操作 JNI UI 的时候,有时候需要用 CallInUiThread 来执行
例如如下的代码。
procedure Toast(const Msg: string; duration: TToastLength);
var
  ToastLength: Integer;
begin
  if duration = ShortToast then
    ToastLength := TJToast.JavaClass.LENGTH_SHORT
  else
    ToastLength := TJToast.JavaClass.LENGTH_LONG;
  CallInUiThread(
    procedure
    begin
      TJToast.JavaClass.makeText(SharedActivityContext, StrToJCharSequence(Msg),
        ToastLength).show
    end);
end;



最后请大家看看 Androidapi.inc 和 Androidapi.NativeWindowJni 的内容,大家就知道如何调用 *.so 文件了。


(C)(P) By Flying Wang 2013-10-05
辅助 By 爱吃猪头肉。

上面的版权声明还有代码中的相关声明请不要移除。

JNI_翻译_转_Delphi_的_经验_方法.txt
作者: wang_80919    时间: 2018-8-18 10:19
请一定要看下面的内容。

关于 调用 JNI JAR java 的说明和注意事项,调用第 靠写不下了
http://www.2pascal.com/forum.php ... =1384&fromuid=4
(出处: 2Pascal-新时代的Pascal)

作者: wang_80919    时间: 2018-8-18 10:26
曾经看到一个 JAVA 代码

[mw_shl_code=java,true]    try {
       Class c = Class.forName("android.os.SystemProperties");
               Method get = c.getMethod("get", String.class);
               Log.i("xxxxxxxx", "the xxxxxxxx:" + (String) get.invoke(c, "xxxxxxxx")));
            } catch (Exception e) {
                 e.printStackTrace();
        }[/mw_shl_code]

非常简单的就能手动翻译成如下 内容红框内。
[attach]1058[/attach]

然后调用代码就简单多了。
[mw_shl_code=delphi,true]        oStr := TJSystemProperties.JavaClass.get(StringToJString('xxxxxxxx'));
        if oStr <> nil then
          AStr := JStringToString(oStr).Trim;[/mw_shl_code]

如果你无法从 主贴中 看出翻译方法,就放弃吧,你不适合搞开发。
作者: flymarshal    时间: 2018-8-18 10:27
已读,空了再仔细学习下




欢迎光临 2Pascal-新时代的Pascal (http://2pascal.com/) Powered by Discuz! X3