因為工作需要,所以在這做個筆記
也順便學習一下
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://www.cs.cornell.edu/courses/cs4670/2010fa/projects/final/results/group_of_acc269_ty244_yc563/cs4670_final.html (網頁沒有問題,請放心)
他給的source code是需要用NDK編譯OpenCV的,但是我一直沒辦法把環境架起來,請問您能否教一下如何處理這問題?
勞煩之處,非常感謝。謝謝。
版主回覆:(07/09/2013 09:02:17 AM)
你用Windows / Mac / Linux ?
我是使用windows系統,謝謝
版主回覆:(08/14/2013 02:54:20 PM)
因為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!
求幫助…
版主回覆:(08/14/2013 02:38:09 PM)
這個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
版主回覆:(08/14/2013 01:09:18 PM)
感謝您的分享和補充
版主你好,我按照你的步驟做,遇到了兩個問題:
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
版主回覆:(08/13/2014 01:59:11 AM)
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指令查看當前目錄