關於 調用 JNI JAR 的說明和注意事項,調用第三方 JAR SDK 和 翻譯 安卓 JAVA 代碼 的說明 V2015.6.10


關於 調用 JNI JAR 的說明和注意事項,調用第三方 JAR SDK 和 翻譯 安卓 JAVA 代碼 的說明 V2015.6.10php

轉載請標明出處,不然死全家。
選擇【複製連接】便可獲得出處。html


(* ************************************************ *)
(*                         *)
(*                         *)
(*  設計:愛吃豬頭肉 & Flying Wang 2015-04-15   *)
(*      上面的版權聲明請不要移除。      *)
(*                         *)
(* ************************************************ *)java

本人所在的羣(① FireMonkey[移動開發] 165232328)android


對於安卓系統
谷歌 API 提供的是 JNI 接口。
第三方 JAR SDK 提供的也是 JNI 接口。api

您的手機,通常會內置 安卓 SDK 的大部分接口。
可是有些 API 可能沒有內置。數組

FMX 安卓工程默認會給您提供多個 谷歌的 SDK 。
所以 大部分 谷歌的 JAR,您都不須要本身去找。ide

對於 第三方的 SDK 來講。
基本上分爲 jar + so、純jar 和 純so 三種。
純so 就是相似於 dll 的接口。只須要注意 dll 是 stdcall 類型,其餘全部平臺都是 cdecl 類型。 不寫是不行的。由於 delphi 不寫的是默認的 pascall 類型。
若是你不會調用 dll 那麼也就不會調用 so 。
本文不討論調用 so。函數

可是告訴大家 SO 文件文件的路徑:編輯 RemotePath 列。路徑是(xe5) library\lib\armeabi (xe6或以上) library\lib\armeabi-v7a
以上路徑的 so 通常是給 jar 加載用的。
本身加載能夠用 LoadLibaray 或者 dlopen 。記得用對應的 FreeLibaray 和 dlclose
也能夠像定義 DLL 同樣,定義 so 的函數接口。這樣就不用寫代碼加載了。
若是隻有你本身用,也能夠發佈到其餘你能訪問的路徑。可是,只能寫代碼加載了,路徑要寫完整。工具


對於 jar 不管它有沒有提供 so 。咱們都只能使用 jar 的對外接口。oop

除非是安卓基本 API,或者是 DELPHI 已經提供的 JAR 。不然 其餘的 JAR ,只要你用到了,就必須加入到 安卓的 工程中。 具體如何添加,請自行百度。

即使是 基本 API,EMBT 也沒有所有給你們 轉成 JNI 接口。
因此當你用到一個 EMBT 沒翻譯的 API 的時候,請自行用 工具 翻譯。

翻譯:能夠理解爲 語言的轉換,接口的導出,也就是變成 pascal語法的 pas 文件

當你獲得一個 JAR 的時候,請用工具翻譯成 pascal 文件。
目前推薦 2 個 工具。
1. 官方的 java2op.exe 。支持 .jar .java .class 三種格式的文件。
2. 愛吃豬頭肉的 JarOrClass2pas 。支持 .jar .class 兩種格式的文件。

其餘工具都是垃圾。千萬別用。不然活該。

當你用工具獲得 pas 文件後,注意:
1. 通常會獲得 大量的 無用的,錯亂的,重複的 接口。請將它們刪除。
2. 即使是沒問題的接口,若是用不到,也請刪除。
3. 轉換工具會寫出一些 uses 的單元,這些單元可能不存在。

對於jar 或 class 文件引用了別的 jar,就容易出現不認識的 unit 的 uses。請找到這些 jar 繼續翻譯。


當你 jar 已經加入到 安卓項目中。
jni pas 文件已經準備好,也加入到 安卓項目中。
就能夠開始調用 jar 接口了。


接下來講明下基本的類型變化。

Int 就是 Integer ,不少基本對象你們均可以本身想到。
string 是 JString 。
Uri 要翻譯成 Jnet_Uri。
上述兩個類型 EMBT 提供了互相轉換的函數。
還有個別的其餘改變名稱的類型。這裏就沒法一一列舉了。若是 你發現一些 類名 EMBT 應該提供了,可是找不到,請經過 Signatur 在 Find in Files 對話框中查找。
例如 搜索 java/lang/Class 能夠發現 JCalss 也更名了。

int [] 就是 TJavaArray<Integer> ,基本類型用 TJavaArray<>。
可是 string [] 是 TJavaObjectArray<JString>,對象類型的通常都用 TJavaObjectArray<>。

ArrayList<String> 要翻譯成 JArrayList
並且任何 ArrayList<endingIntent> 也就是 ArrayList<某對象> 都要翻譯成 JArrayList。

有些類型的名稱比較特別。例如 java 的 Phone 類型 EMBT 已經翻譯成了 JCommonDataKinds_Phone。這是由於 Phone 是 CommonDataKinds 的內部類。


數據類型 OK 了。那麼就談談 Java 類的 構造函數 類成員 類方法 和 普通成員 普通方法。
若是不懂這些,請自行百度。建議好好看看 面向對象開發 課程。

Java 類通常會提供默認構造函數,到了 pas 裏頭,他的函數名叫 init 小寫。默認不帶參數。
可是不少 java 類 會重載 構造函數,提供帶參數的版本。


接下來,咱們須要百度下你打算使用的 jni 的 demo 。
安卓開發,最大的優點就是,網上全是 demo ,雖然是 java 語言的。

找到 demo 代碼以後,你就須要 按照 代碼的邏輯,進行語言的翻譯。

下面提供幾種常見的 代碼 翻譯。

1. 某對象的 構造。
1.1 java 代碼
xxx x = new xxx(參數或沒有參數);
1.2 翻譯代碼
var
x: Jxxx;
begin
x := TJxxx.JavaClass.init(參數或沒有參數);
if x = nil then 出錯了。
end;
上述代碼,演示了構造函數的使用。 注意:
1.3 x 是 Jxxx
1.4 TJxxx.JavaClass 是 TJ 開頭的。JavaClass. 能提供給你 這個類的全部類方法,類成員,包括構造函數。
1.5 不是全部類都提供默認構造函數的。

2. 某對象的 非默認構造,使用類方法的構造。
2.1 java 代碼
xxx x = new xxx.yyy(參數或沒有參數);
2.2 翻譯代碼
var
x: Jxxx;
begin
x := TJxxx.JavaClass.yyy(參數或沒有參數);
if x = nil then 出錯了。
end;
上述代碼,演示了使用類方法來獲得類 xxx 的對象。

3. 經過其餘類的對象,來獲得對象 x。
3.1 java 代碼
xxx x = yyy.zzz(參數或沒有參數);
3.2 翻譯代碼
var
x: Jxxx;
begin
x := yyy.zzz(參數或沒有參數);
if x = nil then 出錯了。
end;
很是簡單,就是加了個冒號。yyy 是另外一個對象。 zzz 能夠是 yyy 對象的類方法、類成員、普通方法或普通成員。


4. 經過強制類型轉換,來獲得對象 x,通常不經常使用。
4.1 java 代碼
xxx x = (xxx)yyy.zzz(參數或沒有參數);
zzz 返回的類型不是 xxx,也不是 xxx 的派生類。
4.2 翻譯代碼
var
xLocalObject: JObject;
x: Jxxx;
begin
xLocalObject := yyy.zzz(參數或沒有參數);
或者
xLocalObject := JObject(yyy.zzz(參數或沒有參數));

if xLocalObject = nil then 出錯了。
x := TJxxx.Wrap((Obj as ILocalObject).GetObjectID);
if x = nil then 出錯了。
end;
注意 Wrap 的開頭,也是 TJ
通過 [臺北]wildsky(2590003092) 的驗證
x := TJxxx.Wrap((yyy.zzz(參數或沒有參數) as ILocalObject).GetObjectID);
這樣少一個變量的寫法。部分機器發生閃退。
用兩個變量來完成,就不會發生閃退。

對於某些 JNI 服務
通過 [深圳]機器貓(5909386) 的驗證
TJContext.JavaClass.VIBRATOR_SERVICE
TJActivity.JavaClass.VIBRATOR_SERVICE
用上面個能夠,下面的就會閃退。

J 開頭 TJ 開頭 都是 約定俗成。你也能夠 SB 開頭 TSB 開頭。

默認 TJ 開頭的是 DELPHI 的類型。
J 開頭的纔是 JAVA 的類型。

TJxxx.JavaClass 返回 JxxxClass 類型,這個類型專門表明類方法和類屬性。 Jxxx 則表明類對象的類型。

5. 不經過變量來操做某類型 xxx 的代碼。
5.1 java 代碼
xxx.yyy(參數或沒有參數).zzz(參數或沒有參數);
5.2 翻譯代碼
TJxxx.JavaClass.yyy(參數或沒有參數).zzz(參數或沒有參數);
5.3 xxx 是類型 yyy 是類函數。
5.4 能夠出現 xxx.JavaClass.yyy(參數或沒有參數).zzz(參數或沒有參數).nnn(參數或沒有參數); 這種多級的調用。

6. 已存在對象 xxx 調用他的方法來操做的代碼。
6.1 java 代碼
xxx.yyy(參數或沒有參數);
6.2 翻譯代碼
xxx.yyy(參數或沒有參數);
6.3 xxx 是對象 yyy 是該對象的類函數或成員函數。
6.4 能夠出現 xxx.yyy(參數或沒有參數).zzz(參數或沒有參數); 這種多級的調用。

7. 使用常量
7.1 java 中的常量,通常都是類的成員。並且通常是類的類成員。
7.2 java 代碼
xxx = yyy.zzz;
//xxx 是一個變量 yyy 是一個類名 zzz 是常量名
7.3 翻譯代碼
xxx := TJyyy.JavaClass.zzz;
7.4 也就是說 zzz 被翻譯到了 Jyyy 的 Class 版本的 接口中。
7.4 效果等於
xxx := JyyyClass.zzz;
7.5 不建議用上面的代碼。請按 7.3 的版本寫。

注意,對於咱們 pascal 來講沒有參數括號是能夠省略的。 C JAVA 等是不容許省略的。


對於 數組 TJavaArray<XXXX> 若是你特別想本身創建對象。
能夠寫
var
xxx: TJavaArray<XXXX>;
begin
xxx := TJavaArray<XXXX>.Create(個數);
而後用 xxx.Items[編號] 來訪問。
end;
TJavaObjectArray 也是如上的辦法。
目前沒有發現動態修改數量的辦法。也就是數組的個數,是創建的時候就肯定的。


若是你使用一個方法,發生Segmentation fault(11) 多是對象爲 nil 或者 函數不存在(通常是版本不一樣,有的版本函數就不存在)。

若是你使用一個方法,發生非法操做,說明沒有這個方法(大概是名稱或參數有錯誤)。
若是提示你 java class xxx could not be found,若是是官方 xxx ,那麼是你的手機內部沒提供這個接口,你能夠本身找官方的 jar 文件來加入、
若是是第三方的 xxx,那更簡單了,這個 xxx 對應的 jar 文件,你確定沒加入到 你的工程中。
若是你確認你加了(參考 第二個箭頭),建議作以下操做
打開你的 Android 工程,點菜單項 Project—>Deployment,打開部署子窗口,點 Revert to Default 按鈕,就是那個向左的彎箭頭:
出現 Revert to default 對話框:
選中第一項「Revert for all configurationsthe active platform」,點 OK。
注意:不論其默認選項如何,在這裏都必須選擇其中一個並點OK,不然你的Android程序在調用JAR文件時將會出現「Java Class xxx could not befound」的錯誤。
以上文字來源於 http://blog.sina.com.cn/s/blog_648d306d0102vfgq.html


若是 Objs 是 TJavaObjectArray<Jxxxx> 的 Objs.Items[x] 或者 Objs[x] 發生錯誤(Segmentation fault(11)),那麼就是不能這樣用,改用
TJxxxx.Warp(Objs.GetRawItem(x)) 試試。多謝 [深圳]機器貓(5909386) 的測試。


通常建議
uses
{$IF CompilerVersion >= 27.0} // >= XE6
Androidapi.Helpers,
{$ENDIF}
FMX.Helpers.Android,
Androidapi.JNI.JavaTypes,
Androidapi.NativeActivity;


當須要一個 Activity 對象的時候,咱們只能提供 TAndroidHelper.Activity。由於 FMX 只存在這個一個 Activity。
當須要一個 View 對象的時候,默認的是 TAndroidHelper.Activity.getWindow.getDecorView。其餘的就不知道了。
當須要一個 Context 對象的時候,能夠試試 TAndroidHelper.Context,這是全局的。
當你須要一個 getApplicationContext 對象的時候,能夠試試 TAndroidHelper.Context.getApplicationContext,這是由於你不懂本身看源碼,不是個人錯。
{$IF CompilerVersion >= 30.0} // >=RAD10
TAndroidHelper.Activity
TAndroidHelper.Context
{$ELSE}
SharedActivity
SharedActivityContext
{$ENDIF}

不管如何,當你獲得一個 java 對象必定要先檢查 是否是 nil,不然輕則提示錯誤,重則閃退。
EMBT 常常忘了檢查,因此就閃退,例如你在窗體中放了一個 IAP 支付控件,不少手機上都會閃退,就是由於沒檢查 nil。
若是是正在開發的 APP 在任何機器上閃退,特別是舊版本升級來的,別人複製給你的。通常是 發佈信息混亂,形成的。工程的 發佈(部署)信息須要【從新加載】。Deployment 須要 Revert to Default
若是是任何APP。包括新建的空 APP,在特定的機器上一運行就閃退。說明是一個 BUG。請在本羣的 不看後悔 系列中 解壓 找 XE 修復 APK 啓動,提示 Cannot deploy," " file not found.txt。


當你使用一個 jni 對象的時候。若是是個能夠顯示的對象。不少時候須要:
CallInUiThread(
procedure
begin
jni 代碼。
end);
有時候 還得換成 CallInUIThreadAndWaitFinishing。
只有這樣 代碼纔不會死鎖。
也就是 若是不這麼寫。你的 APP 就會出現 未響應。
有些不是顯示的 jni 對象,也須要這樣寫。不過不常見。
若是你收到一個錯誤 CalledFromWrongThreadException,就是須要 CallInUiThread 了。

若是收到的是
Can't create handler inside thread that has not called Looper.prepare()
也是須要 CallInUiThread 了。多謝 [新會]supermay(15832782) 測試。

注意:不要將大片代碼進入上述的代碼塊中。儘可能減小相關代碼。最好是用 DEBUG ,找出彈出這類錯誤提示的代碼行。不會 DEBUG 請看書。

在 安卓的世界裏,回調函數是不存在的。可是可使用接口來作到回調。
通常這種接口 會被定義成 Listener。

當你須要繼承(實現)一個 java 接口的時候,就須要查看有關代碼了。
使用 DELPHI IDE,在 Search 菜單打開 Find In Files 對話框。
輸入搜索關鍵字
= class(TJavaLocal,
搜索範圍 Search in directories
選擇到你的安裝目錄的源代碼目錄,選中 Indude subdirectories
好好找吧。
你會找到好多代碼。
他們都是繼承(實現) java 接口的好例子。

按照面向對象的說法 接口必須實現。因此上面找到的代碼是必須的。
而後定義出這個類的 對象,就能夠當參數在 jni 中使用了。
不過這種類型,是 delphi 的實現,因此別忘了 free。

我的建議你們好好研讀【unit System.Android.Bluetooth;】。

有時候 接口提供的 回調函數 多是在線程中運行的。
這時候你實現這個函數的時候,要注意。
UI 對象 不管是 FMX 的仍是 jni 的。你都須要線程同步。

線程同步的簡單方法就是。
...
//線程或回調函數裏的一些代碼。
TThread.Synchronize(nil, //或者 用線程本身的同步函數。 Synchronize(
procedure
begin
//你的界面交互代碼。
end);
//繼續線程或回調函數的代碼。
...

若是你 DEBUG 中收到了 Bitmap size too big 的提示,有可能就是 該用同步,沒同步形成的。


有了以上知識,你基本上翻譯 java 的代碼,就不成問題了。
別說,你不會 順序、判斷、循環、函數調用。

若是你想了解一個 第三方 view 是如何顯示到 FMX 中的。
能夠參考 unit FMX.WebBrowser.Android 和 unit FMX.Media.Android。

另外,不少操做,都須要對應的權限,別忘了加上。

對於 4.4 以上的系統,想要訪問外置存儲卡。須要加上
<uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE"/>
這個權限。
目前 IDE 沒有提供。
您須要在 AndroidManifest.template.xml 文檔中,本身加入,用 IDE 就能打開,找到 <%uses-permission%>,將上述權限文字加入到 這行下面就行。
可是,不保證全部機器都有效。


若是發現須要使用 安卓下面的 命令行,例如執行 su sh ping 等。
能夠參考 QDac http://blog.qdac.cc 的代碼,可能叫 QRuntime。
也能夠參考 本羣的 重啓你的手機 源代碼。


對於學習 翻譯 安卓 java 代碼爲 pascal 。
最好先看 EMBT 的源代碼。而後看 EMBT 的 Samples。
也能夠好好看 本羣的羣共享。
裏頭好多調用 jni 的 DEMO。

 

相關工具
JarOrClass2Pas FlyingWang V1.0.2015.517 附贈 java 轉 Jar.zip
http://www.2pascal.com/forum.php?mod=viewthread&tid=891&fromuid=4
(出處: 2Pascal-新時代的Pascal)


RAD10RTM 加載 jar 存在 BUG
https://quality.embarcadero.com/browse/RSP-12335
QC 中有解決辦法。
建議去 EMB 官網註冊 EDN 帳號,便可登陸。
上面的 BUG 新版本已經 FIX 了。

http://www.2pascal.com/forum.php?mod=viewthread&tid=1384&fromuid=4

相關文章
相關標籤/搜索