Android JNI調用(三)

  最後需要說的就是,在偶傳上來的代碼中,可能會發現有一個叫做libcutils.a的編譯好的靜態庫,這個東西就“說來話長”了,主要原因是偶在做實驗的時候,還沒有ndk發佈出來,android手機裏面也沒個gdbserver之類的工具,調試起來十分痛苦。偶認爲再怎麼弱,也要輸出點東西到logcat吧?!因此,從android-platform的平臺源代碼中提取了cutils的頭文件,直接把android平臺編譯出來的二進制.a文件拷貝出來,鏈接到偶自己的“土法”生成的so庫裏面,這樣就可以調用libcutils.a中定義的log函數,就可以直接通過聯機的logcat查看jni中的log日誌輸出,很爽!ndk的文檔中承諾,在未來的android ndk開發包中會提供在線調試的功能。

    到此爲止,“土法”編譯和編寫jni的方法已經基本記錄和講解完畢。一定能夠對ndk的本質有了新的認識。而不是那裏面readme和howto文檔中的幾行字,修改android。mk之類云云。。。
  當然有了上面的這些底層編譯的探索,加上ndk裏面提供的。h和若干運行時庫,甚至android平臺源代碼裏面編譯出來的靜態二進制包,jni幾乎可以實現任何功能。
  還是那句話,“潘多拉”的盒子一旦打開,能否控制得住,就不是google這樣的公司能夠左右的了。
  等有時間再來寫寫關於使用google的ndk來編寫和調試jni模塊的方法。。。
  其實主要是關於ndk的一些編譯選項的研究和翻譯(其實人家google的文檔已經說的很清楚了)。偶選用的測試環境是slackware 12。0 + android 1。5 r1 for linux + jdk 1。6。0_12,ndk選用的是android 1。5 ndk r1這個版本的(直接解壓就行,免安裝的)。
  1、從ndk安裝說起
  ndk安裝的時候需要運行一個~/android-ndk-1。5_r1/build/目錄下面的一個叫做host-setup。sh的腳本。大略讀了一下這個腳本,發現這個主要是用來生成out/host/host/config。mk文件的。主要用於指定用戶操作系統的判斷以及支持的編譯器類型(設置makefile中的cc,ar,ld之類的變量)
  ndk的目錄介紹。
  2、ndk的目錄結構分析
  進入android-ndk-1。5_r1目錄,看到如下目錄結構:
  GNUmakefile: 標準的makefile格式的文件,用於引用build/core/main。mk的編譯腳本。
  README。TXT:基本的說明,沒啥大用,真正有用的文檔都在docs目錄下面。
  apps/:存放帶有jni接口的android工程目錄(工程裏面有利用native關鍵字定義的java函數)
  build/:存放着幾乎所有的ndk編譯相關的腳本以及必要的靜態鏈接庫。
  docs/:存放這ndk的所有“官方”文檔,每一篇文檔對於jni編寫者來說這裏面的任何一點點資料都是無價的。
  out/:存放一些中間的臨時文件,例如jni的。c/。cpp文件編譯過程中產生的。o文件等。
  sources/:存放jni文件的。c/。cpp的源代碼文件。
  3、基本的使用方法
  (1)創建一個android工程
  進入apps目錄,運行如下命令:
  android create project --target 2 --package com。TWM --activity NDKTest --path 。/NDKTest/project
  通過命令行創建一個叫做NDKTest的activity,注意這裏的--path需要設置爲。/XXXXX/project這個目錄,這個XXXXX目錄主要是爲了ndk的make區分不同項目和工程使用的。編寫Application。mk文件的時候,一定要把Application。mk寫到這個XXXXX目錄下面。
  $NDK/apps/<myapp>/Application。mk
  另外,編譯jni庫的時候使用的命令也是如此:
  make APP=<your app name>
  這裏的<your app name>實際上也是這個XXXXX目錄。
  (2)爲工程添加一個jni的java調用接口
  進入app/NDKTest/project/src/com/TWM/NdkTest目錄,建立一個新的java文件(例如:NDKJni。java),然後把代碼寫成類似下面這個樣子:

java代碼:
  1. package com.TWM.NdkTest ; 
  2. public class NDKJni {
  3. public native int MyFunc(int a, int b) ;
  4. static {
  5. System.loadLibrary("NDKjni") ; 
  6. }


複製代碼

       這裏的MyFunc由於是使用native修飾,因此,這個MyFunc函數是一個調用jni的函數。
  (3)爲java工程編寫Application。mk文件
  該文件主要放在app/NDKTest目錄下,用於告知ndk的編譯腳本,當前的程序需要哪個jni模塊。
  看上去應該是這個樣子的:
  APP_PROJECT_PATH := $(call my-dir)/project   ---> 當前目錄下的project目錄包含了jni模塊的java接口
  APP_MODULES      := NDKTest                               --->當前模塊的名字叫做NDKTest
  (4)弄清楚java程序的包層次
  以當前的這個project爲例,就是上面代碼中的package com。TWM。NdkTest,定義的類名爲NDKJni。因此,根據這個包的層次,可以根據jni文件的函數命名規則定義函數:
  JNIEXPORT jint JNICALL Java_com_TWM_NdkTest_NDKJni_MyFunc(JNIEnv * env, jobject thiz, jint a, jint b) ;
  當然,手工根據包層次定義jni函數還是很痛苦的,可以藉助於javah工具:
  mkdir -p apps/NDKTest/project/jni
  cd apps/NDKTest/project/jni
  javah -classpath "/bin/classes" com。TWM。NdkTest。NDKJni
  然後就會自動生成一個叫做com_TWM_NdkTest_NDKJni。h的文件,裏面的內容基本上跟手工生成的差不多:

java代碼:
  1. /* DO NOT EDIT THIS FILE - it is machine generated */

  2. #include <jni.h>

  3. /* Header for class com_TWM_NdkTest_NDKJni */
  4. #ifndef _Included_com_TWM_NdkTest_NDKJni
  5. #define _Included_com_TWM_NdkTest_NDKJni
  6. #ifdef __cplusplus
  7. extern "C" {

  8. #endif

  9. /*
  10. * Class: com_TWM_NdkTest_NDKJni
  11. * Method: MyFunc
  12. * Signature: (II)I
  13. */
  14. JNIEXPORT jint JNICALL Java_com_TWM_NdkTest_NDKJni_MyFunc
  15. (JNIEnv *, jobject, jint, jint);

  16. #ifdef __cplusplus
  17. }
  18. #endif


複製代碼
發佈了71 篇原創文章 · 獲贊 30 · 訪問量 60萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章