因為工作需要,所以在這做個筆記
也順便學習一下

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的環境一樣

Screen Shot 2013-01-15 at 5.54.38 PM  

但我們是要安裝CDT,這裡小小的不一樣

Work with:這欄打入

http://download.eclipse.org/tools/cdt/releases/indigo

然後勾選CDT Main Features安裝C/C++開發環境

Screen Shot 2013-01-15 at 6.03.51 PM  

接下來就是Next大法,沒啥好解釋的

Screen Shot 2013-01-15 at 6.04.54 PM  Screen Shot 2013-01-15 at 6.05.11 PM  

按下Finish,馬上就開始下載了

Screen Shot 2013-01-15 at 6.06.12 PM  

安裝完成的訊息問你要不要重開Eclipse,就照預設值Restart Now

Screen Shot 2013-01-15 at 6.11.25 PM  

但還沒完,

Eclipse重開了以後,再次Install New Software回到這個畫面

這次填上ADT的網址

https://dl-ssl.google.com/android/eclipse/

這次勾選NDK Plugins

Screen Shot 2013-01-15 at 6.14.40 PM  

剩下那些廢圖我就不重覆貼了

CDT和NDK安裝只多了一個警示框,按OK繼續安裝

Screen Shot 2013-01-15 at 6.17.38 PM  

Eclipse再次重開之後,在Eclipse > Preferences視窗中 

Screen Shot 2013-01-16 at 8.55.38 AM   

Android > NDK的頁籤中

選取你下載解壓後的NDK路徑
我是把它跟android sdk放在一起,有必要的話可以參考我的路徑

 Screen Shot 2013-01-16 at 8.55.24 AM  

環境設定完,就馬上開始寫程式摟!

新增專案

就像是老樣子,New > Android Project,開啟一個Android專案
因為這裡講到爛掉,所以我跳快一點

Screen Shot 2013-01-16 at 9.03.08 AM  

我開了一個專案

Application Name: HelloNDK
Package Name: com.J_Test.hellondk

Activity Name: MainActivity
Layout Name: activity_main

加入JNI支援

這裡就跟一般的Android專案不一樣

在你的專案按下右鍵 > Android Tools > Add Native Support...

Screen Shot 2013-01-16 at 9.07.37 AM  

出現一個視窗
提示你JNI編譯輸出的名稱

Screen Shot 2013-01-16 at 9.13.00 AM  

按下Finish之後,會發現你的專案目錄多了Includes和jni資料夾這二項

Screen Shot 2013-01-16 at 9.20.29 AM

 

設計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;

}

先不管JNIEXPORTJNICALL等關鍵字

jintjobjectjstring等型別,這些是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

就可以解決

Screen Shot 2013-01-17 at 2.58.43 AM  

若在使用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資料夾尋找

若沒有,請重新編譯一次(輸出一次到模擬器是個不錯的選擇)

----------

最後,感謝這些參考資料,都是來自世界各個角落的精華
讓我好好學了一課!

device-2013-01-17-023803  

對了,有了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

arrow
arrow
    全站熱搜

    Johnny 鋼鍊 發表在 痞客邦 留言(6) 人氣()