因為工作需要,所以在這做個筆記
也順便學習一下
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/