因為工作需要,所以在這做個筆記
也順便學習一下
JNI全名叫Java Native Interface,意思就是說
這不是Android獨有的東西,純Java的應用程式也可以寫
(但本例還是以Android下的JNI為主)
以下是用Mac去搭建的,同樣的做法Linux也可以
環境準備
假設你已經有開發過Android的經驗
有Android SDK+Eclipse最基礎的環境
這樣你需要加裝NDK(Native Development Kit) 和CDT(C/C++ Development Toolkit)
首先,先到Android官網下載NDK,解壓縮後存在一個路徑
下載網址:
http://developer.android.com/tools/sdk/ndk/index.html
像本例用Mac搭建,所以下載Mac版本
在Help > Install New Software 就像是你之前在安裝Android的環境一樣
但我們是要安裝CDT,這裡小小的不一樣
Work with:這欄打入
http://download.eclipse.org/tools/cdt/releases/indigo
然後勾選CDT Main Features安裝C/C++開發環境
接下來就是Next大法,沒啥好解釋的
按下Finish,馬上就開始下載了
安裝完成的訊息問你要不要重開Eclipse,就照預設值Restart Now
但還沒完,
Eclipse重開了以後,再次Install New Software回到這個畫面
這次填上ADT的網址
https://dl-ssl.google.com/android/eclipse/
這次勾選NDK Plugins
剩下那些廢圖我就不重覆貼了
CDT和NDK安裝只多了一個警示框,按OK繼續安裝
Eclipse再次重開之後,在Eclipse > Preferences視窗中
Android > NDK的頁籤中
選取你下載解壓後的NDK路徑
我是把它跟android sdk放在一起,有必要的話可以參考我的路徑
環境設定完,就馬上開始寫程式摟!
新增專案
就像是老樣子,New > Android Project,開啟一個Android專案
因為這裡講到爛掉,所以我跳快一點
我開了一個專案
Application Name: HelloNDK
Package Name: com.J_Test.hellondk
Activity Name: MainActivity
Layout Name: activity_main
加入JNI支援
這裡就跟一般的Android專案不一樣
在你的專案按下右鍵 > Android Tools > Add Native Support...
出現一個視窗
提示你JNI編譯輸出的名稱
按下Finish之後,會發現你的專案目錄多了Includes和jni資料夾這二項
設計JNI函數名稱
HelloNDK.cpp和Android.mk是我們今天編輯的重點
首先,先將HelloNDK.cpp 重新命名為 HelloNDK.c
然後編輯Android.mk
LOCAL_PATH := $(call my-dir)
include$(CLEAR_VARS)
LOCAL_MODULE := HelloNDK
LOCAL_SRC_FILES := HelloNDK.c
include$(BUILD_SHARED_LIBRARY)
這個HelloNDK和HelloNDK.cpp,就是先前填寫.so的名稱
將檔名改成對應的名稱
我們在MainActivity加入以下:
static
{
System.loadLibrary("HelloNDK");
}
public native String helloString();
public native int plus(int a, int b);
public native int multiply(int a, int b);
把我們要呼叫C使用的方法界面,加個native修飾字
此例
helloString() 回傳一個測試字串
int plus(int a, int b) 做兩數相加
int multiply(int a, int b) 做兩數相乘
最後加上System.loadLibrary 載入我們即將要自定的JNI Module Name
產生Header檔案
我們用javah指令產生JNI對應的Header file
但它會讀取編譯後的*.class檔案
所以在這之前,請將專案整個Build過一遍
請先確定專案下的bin/classes+package名稱的對應之資料夾
是否有編譯後的*.class檔(這在Eclipse專案目錄下不會看見,用Finder去開)
本例是HelloNDK/bin/classes/com/J_Test/hellondk/MainActivity.class
接下來,打開Terminal
切換到專案的根目錄
使用這個指令
javah -d jni -classpath bin/classes <package名稱+class名稱>
在本例就是
javah -d jni -classpath bin/classes com.J_Test.hellondk.MainActivity
順利執行之後,回到Eclipse,專案視窗按F5重新整理
在jni資料夾會多出com_J_Test_hellondk_MainActivity.h這個檔,內容為以下
/* DO NOT EDIT THIS FILE - it is machine generated */
#include<jni.h>
/* Header for class com_J_Test_hellondk_MainActivity */
#ifndef _Included_com_J_Test_hellondk_MainActivity
#define _Included_com_J_Test_hellondk_MainActivity
#ifdef __cplusplus
extern"C" {
#endif
/*
* Class: com_J_Test_hellondk_MainActivity
* Method: helloString
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_J_1Test_hellondk_MainActivity_helloString
(JNIEnv *, jobject);
/*
* Class: com_J_Test_hellondk_MainActivity
* Method: multiply
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_J_1Test_hellondk_MainActivity_multiply
(JNIEnv *, jobject, jint, jint);
#ifdef __cplusplus
}
#endif
#endif
好吧,我承認它是真的醜了點
但還是可以寫C程式的
想想當初怎麼寫C程式,一個標頭檔...只宣告參數型別
但沒宣告參數名稱
實作JNI
編輯HelloNDK.c內容
#include<jni.h>
#include<string.h>
#include"com_J_Test_hellondk_MainActivity.h"
JNIEXPORT jstring JNICALL Java_com_J_1Test_hellondk_MainActivity_helloString(
JNIEnv *env, jobject thiz) {
return (*env)->NewStringUTF(env, "Hello from JNI !");
}
JNIEXPORT jint JNICALL Java_com_J_1Test_hellondk_MainActivity_multiply(
JNIEnv *env, jobject thiz, jint a, jint b) {
jint total = 0;
total = a * b;
return total;
}
先不管JNIEXPORT和JNICALL等關鍵字
jint、jobject、jstring等型別,這些是Java裡給的型別
這裡有張對照表
| Java型別 | 型別表示 | 字節大小(bit) |
| boolean | jboolean | 8, unsigned |
| byte | jbyte | 8 |
| char | jchar | 16, unsigned |
| short | jshort | 16 |
| int | jint | 32 |
| long | jlong | 64 |
| float | jfloat | 32 |
| double | jdouble | 64 |
| void | void | --- |
至於String、物件、陣列...等,處理起來就較複雜些
----------
疑難排解
在編譯時我有遇到
Method 'NewStringUTF' could not be resolved
解決辦法,
在專案下按右鍵 Properties >C/C++ General > Paths and Symbols 的頁籤中
按下Add...選擇
<ndk路徑>/platforms/android-14/arch-arm/usr/include
/Users/johnny/android-sdks/android-ndk/platforms/android-14/arch-arm/usr/include
就可以解決
若在使用javah遇到類似的訊息
error: cannot access com.J_Test.hellondk.MainActivity
class file for com.J_Test.hellondk.MainActivity not found
javadoc: error - Class com.J_Test.hellondk.MainActivity not found.
Error: No classes were specified on the command line. Try -help.
請先確定*.class的位置
不含package的資料夾路徑
要從專案裡的bin資料夾,或是bin/classes資料夾尋找
若沒有,請重新編譯一次(輸出一次到模擬器是個不錯的選擇)
----------
最後,感謝這些參考資料,都是來自世界各個角落的精華
讓我好好學了一課!
對了,有了function name,要寫個測試程式應該不難吧
這種艱巨的任務,就交給你了!(笑)
參考資料:
http://androidcookbook.com/Recipe.seam?recipeId=77
http://changyy.pixnet.net/blog/post/29469121
http://changyy.pixnet.net/blog/post/29437517
http://developer.android.com/training/articles/perf-jni.html
http://87showmin.blogspot.tw/2009/06/java-java-native-interfacejni.html
https://github.com/androidcook/Android-Cookbook-Examples/tree/master/NdkDemo
http://java.chinaitlab.com/JDK/36678.html
http://blog.csdn.net/ljlsunny/article/details/5753006
http://electrofriends.com/qna/jni-faq/convert-jstring-cstyle-string-vice-versa/
http://blog.csdn.net/gavinr/article/details/7343324

你好,請問版主還有在經營嗎!? 我想請教問題,我想執行這個網站的程式http://www.cs.cornell.edu/courses/cs4670/2010fa/projects/final/results/group_of_acc269_ty244_yc563/cs4670_final.html (網頁沒有問題,請放心) 他給的source code是需要用NDK編譯OpenCV的,但是我一直沒辦法把環境架起來,請問您能否教一下如何處理這問題? 勞煩之處,非常感謝。謝謝。
你用Windows / Mac / Linux ?
我是使用windows系統,謝謝
因為OpenCV我也沒用過,感覺是個很有趣的東西:) 以NDK來說... 除了文章提到的NDK和Eclipse的插件 Windows的話,還要加裝cygwin 安裝詳情我就不清楚了,因為我一路也都用mac系統 有空我裝看看Windows版的 如果真的在Windows上架不起來 不彷在Windows加裝個虛擬機,裝台Linux虛擬機吧:)
版主,之前我有讀過你的一篇HTTP與POST的文章,兩篇我都讀過了 與本機的資料庫連線沒有問題, 但是最近因為移到網路的伺服器端,要連結遠端的伺服器 就出現錯誤了, 我移除了判斷if (httpResponse.getStatusLine().getStatusCode() == 200) 因為沒有移除,就直接拋出錯誤了.. 我直接回傳strResult 顯示的錯誤碼如下 406 Not Acceptable This request is not acceptable Powered By LiteSpeed Web Server LiteSpeed Technologies is not responsible for administration and contents of this web site! 求幫助...
這個if就是判斷是否正確的回傳我們要的網頁(HTTP 200 OK) 現在你的HTTP代碼為406 Not Acceptable 有些機器是HTTP 500 Internal Server Error 或是最常見的404 Not Found 這都是要處理的 你的406問題我覺得這個問題應該是出在Server
很感謝版主的分享~讓我的觀念比較清楚一些 關於最後你有提到 NewStiringUTF的問題,應該是C跟C++的問題 我參考這篇 http://stackoverflow.com/questions/15899813/eclipse-method-newstringutf-could-not-be-resolved
感謝您的分享和補充
版主你好,我按照你的步驟做,遇到了兩個問題: 1.在連接http://dl-ssl.google.com/android/eclipse/ 時並沒有出現 NDK plugin沒有出現 2.在執行 javah -d jni -classpath ...... 時 它顯示以下錯誤: Error: cannot access android.app.Activity class file for android.app.Activity not found
1. 它的選項改到Development tools裡了 Development tools > Android Native Development tools 2. 看起來可能是路徑有錯 javah -d jni -classpath bin/classes com.J_Test.hellondk.MainActivity 它搜尋的資料夾是bin/classes資料夾 PackageName+ClassName為 com.J_Test.hellondk.MainActivity 所以要下這指令的所在目錄要在專案最上層的目錄(與AndroidManifest.xml同一層) 假設專案資料夾名稱為hellondk 所以Terminal所在的目錄應該為 /..(您workspace的路徑).../hellondk/ 而不是以下這些 /..(您workspace的路徑).../hellondk/bin/classes/com/J_Test/hellondk/ /..(您workspace的路徑).../hellondk/bin/classes/ /..(您workspace的路徑).../hellondk/bin/ 可以用pwd指令查看當前目錄
原本在搜尋引擎找出一堆 Blog 文章,不知哪幾篇值得花時間一看, 後來用 PTT搜尋引擎,輾轉看到您這的好文而有緣來到這, 謝謝您用心分享有價值的內容, 也回饋給您這實用的主題排名網站資訊,可查看與您 Blog 內容相關的排名好文,應該對寫 Blog 也有所幫助,期待您持續產出好文章 ^^ https://searchptt.cc/