http://blog.csdn.net/xyz_lmn/article/details/6955031
android支持使用NDK開發C程序,關於配置NDK環境問題應該不用再贅述了,這個網上有很多,這裏通過一篇實例來講述簡單的JNI開發,大家可以參考這篇文章(Get Your Eclipse-Integrated NDK On!)搭建Eclipse編譯C語言爲so文件的開發環境。
native方法實現步驟如下:
1、在Java中聲明native()方法,然後編譯(javac);
2、用javah產生一個.h文件;
3、編寫包含.h文件的c文件
4、編譯c文件
5、使用編譯成功的so文件。
第一步:
1、聲明native方法
- public class Printf_Jni {
- static {
- System.loadLibrary("com_nedu_jni_helloword_printf-jni");
- }
- public native void printHello();
- }
2、javac編譯
進入java文件所在路徑,調用javac命令,如圖:
第二步:使用javah命令生成.h頭文件,如圖:
這個要回到src目錄下,不知道什麼原因,如果在上面的javac路徑下會報錯,如圖:
使用javah命令生成的頭文件如下:
- /* DO NOT EDIT THIS FILE - it is machine generated */
- #include <jni.h>
- /* Header for class com_nedu_jni_helloword_Printf_Jni */
- #ifndef _Included_com_nedu_jni_helloword_Printf_Jni
- #define _Included_com_nedu_jni_helloword_Printf_Jni
- #ifdef __cplusplus
- extern "C" {
- #endif
- /*
- * Class: com_nedu_jni_helloword_Printf_Jni
- * Method: printHello
- * Signature: ()V
- */
- JNIEXPORT void JNICALL Java_com_nedu_jni_helloword_Printf_1Jni_printHello
- (JNIEnv *, jobject);
- #ifdef __cplusplus
- }
- #endif
- #endif
第三步:編寫c文件,代碼如下:
- #include<stdio.h>
- #include <stdlib.h>
- #include "com_nedu_jni_helloword_Printf_Jni.h"
- JNIEXPORT void JNICALL Java_com_nedu_jni_helloword_Printf_1Jni_printHello
- (JNIEnv *e, jobject j)
- {
- printf("Hello world!");
- }
第四步,書寫Android.mk文件,編譯c文件
Android.mk文件如下:
- LOCAL_PATH := $(call my-dir)
- include $(CLEAR_VARS)
- LOCAL_MODULE := com_nedu_jni_helloword_printf-jni
- LOCAL_SRC_FILES :=Printf_Jni.c
- include $(BUILD_SHARED_LIBRARY)
LOCAL_MODULE := com_nedu_jni_helloword_printf-jniLOCAL_MODULE := com_nedu_jni_helloword_printf-jniLOCAL_MODULE 表示so文件名LOCAL_SRC_FILES 需要編譯的文件
按照這篇文章(Get Your Eclipse-Integrated NDK On!)的介紹就可以在Eclipse編譯了。
第五步:使用so文件:
通過下面的代碼加載so文件
- System.loadLibrary("com_nedu_jni_helloword_printf-jni");
通過下面的代碼加載so文件通過下面的代碼加載so文件調用如下:
- Printf_Jni print=new Printf_Jni();
- print.printHello();
http://blog.csdn.net/xyz_lmn/article/details/6956003
前面一篇通過簡單的例子介紹了android中JNI的使用。這一篇從基礎上了解一些Java參數類型與本地參數類型區別。
1) java中的返回值void和JNI中的void是完全對應的哦!(僅僅一個而已)。
2) java中的基本數據類型(byte ,short ,int,long,float,double ,boolean,char-8種)在JNI中對應的數據類型只要在前面加上j就對應了(jbyte ,jshort ,jint,jlong,jfloat,jdouble ,jboolean,jchar)。
3) java中的對象,包括類庫中定義的類、接口以及自定義的類接口,都對應於JNI中的jobject。
4) java中基本數據類型的數組對應與JNI中的j<type>array類型。(type就是上面說的8種基本數據類型)
5) java中對象的數組對應於JNI中的jobjectArray類型。(在java中一切對象、接口以及數組都是對象)
下圖是JNI規範中java數據類型和JNI數據類型的映射圖。
第一幅爲基本數據類型的映射圖:
http://blog.csdn.net/xyz_lmn/article/details/6959545
第二幅爲引用數據類型的映射圖:
一、 首先寫了java文件:
- public class HeaderFile {
- private native void doVoid();
- native int doShort();
- native void doArray(Object[] o );
- native int doInt(int i); //byte ,short ,int,long,float,double ,boolean,char
- native int doInt(double d); //byte ,short ,int,long,float,double ,boolean,char
- native int doInt(Object o);
- native int doInt(double d1,double d2);
- static native int doInt(double d1 ,double d2,double d3);
- static native int doInt(double d1 ,float f,boolean b ,char[] c );
- native int doInt(int[] i);
- native int doInt(int[] i1,double[] i2 );
- static native int doInt(int[] i1,double[] i2 ,Object[] o );
- public native String doString(String s);
- public native Object doObject(Object o );
- public native Enumeration doInterface(Iterator it);
- public native Student doStudent(Student s);
- // native int[] doInt(int[] i); //byte ,short ,int,long,float,double ,boolean,char
- public native String[] doString(String[] s);
- public native Object[] doObjects(Object[] o );
- public native Enumeration[] doInterface(Iterator[] it);
- public native Student[] doStudent(Student[] s);
- public native static Object doAll(int[] i , String[] s , Student[] student );
- }
java文件中包含了private、public、protect等類型的方法,static 方法和非static 方法,返回類型有基礎類型、對象等。
二、下面看一下生成的頭文件:
- /* DO NOT EDIT THIS FILE - it is machine generated */
- #include <jni.h>
- /* Header for class com_nedu_jni_helloword_HeaderFile */
- #ifndef _Included_com_nedu_jni_helloword_HeaderFile
- #define _Included_com_nedu_jni_helloword_HeaderFile
- #ifdef __cplusplus
- extern "C" {
- #endif
- /*
- * Class: com_nedu_jni_helloword_HeaderFile
- * Method: doVoid
- * Signature: ()V
- */
- JNIEXPORT void JNICALL Java_com_nedu_jni_helloword_HeaderFile_doVoid
- (JNIEnv *, jobject);
- /*
- * Class: com_nedu_jni_helloword_HeaderFile
- * Method: doShort
- * Signature: ()I
- */
- JNIEXPORT jint JNICALL Java_com_nedu_jni_helloword_HeaderFile_doShort
- (JNIEnv *, jobject);
- /*
- * Class: com_nedu_jni_helloword_HeaderFile
- * Method: doArray
- * Signature: ([Ljava/lang/Object;)V
- */
- JNIEXPORT void JNICALL Java_com_nedu_jni_helloword_HeaderFile_doArray
- (JNIEnv *, jobject, jobjectArray);
- /*
- * Class: com_nedu_jni_helloword_HeaderFile
- * Method: doInt
- * Signature: (I)I
- */
- JNIEXPORT jint JNICALL Java_com_nedu_jni_helloword_HeaderFile_doInt__I
- (JNIEnv *, jobject, jint);
- /*
- * Class: com_nedu_jni_helloword_HeaderFile
- * Method: doInt
- * Signature: (D)I
- */
- JNIEXPORT jint JNICALL Java_com_nedu_jni_helloword_HeaderFile_doInt__D
- (JNIEnv *, jobject, jdouble);
- /*
- * Class: com_nedu_jni_helloword_HeaderFile
- * Method: doInt
- * Signature: (Ljava/lang/Object;)I
- */
- JNIEXPORT jint JNICALL Java_com_nedu_jni_helloword_HeaderFile_doInt__Ljava_lang_Object_2
- (JNIEnv *, jobject, jobject);
- /*
- * Class: com_nedu_jni_helloword_HeaderFile
- * Method: doInt
- * Signature: (DD)I
- */
- JNIEXPORT jint JNICALL Java_com_nedu_jni_helloword_HeaderFile_doInt__DD
- (JNIEnv *, jobject, jdouble, jdouble);
- /*
- * Class: com_nedu_jni_helloword_HeaderFile
- * Method: doInt
- * Signature: (DDD)I
- */
- JNIEXPORT jint JNICALL Java_com_nedu_jni_helloword_HeaderFile_doInt__DDD
- (JNIEnv *, jclass, jdouble, jdouble, jdouble);
- /*
- * Class: com_nedu_jni_helloword_HeaderFile
- * Method: doInt
- * Signature: (DFZ[C)I
- */
- JNIEXPORT jint JNICALL Java_com_nedu_jni_helloword_HeaderFile_doInt__DFZ_3C
- (JNIEnv *, jclass, jdouble, jfloat, jboolean, jcharArray);
- /*
- * Class: com_nedu_jni_helloword_HeaderFile
- * Method: doInt
- * Signature: ([I)I
- */
- JNIEXPORT jint JNICALL Java_com_nedu_jni_helloword_HeaderFile_doInt___3I
- (JNIEnv *, jobject, jintArray);
- /*
- * Class: com_nedu_jni_helloword_HeaderFile
- * Method: doInt
- * Signature: ([I[D)I
- */
- JNIEXPORT jint JNICALL Java_com_nedu_jni_helloword_HeaderFile_doInt___3I_3D
- (JNIEnv *, jobject, jintArray, jdoubleArray);
- /*
- * Class: com_nedu_jni_helloword_HeaderFile
- * Method: doInt
- * Signature: ([I[D[Ljava/lang/Object;)I
- */
- JNIEXPORT jint JNICALL Java_com_nedu_jni_helloword_HeaderFile_doInt___3I_3D_3Ljava_lang_Object_2
- (JNIEnv *, jclass, jintArray, jdoubleArray, jobjectArray);
- /*
- * Class: com_nedu_jni_helloword_HeaderFile
- * Method: doString
- * Signature: (Ljava/lang/String;)Ljava/lang/String;
- */
- JNIEXPORT jstring JNICALL Java_com_nedu_jni_helloword_HeaderFile_doString__Ljava_lang_String_2
- (JNIEnv *, jobject, jstring);
- /*
- * Class: com_nedu_jni_helloword_HeaderFile
- * Method: doObject
- * Signature: (Ljava/lang/Object;)Ljava/lang/Object;
- */
- JNIEXPORT jobject JNICALL Java_com_nedu_jni_helloword_HeaderFile_doObject
- (JNIEnv *, jobject, jobject);
- /*
- * Class: com_nedu_jni_helloword_HeaderFile
- * Method: doInterface
- * Signature: (Ljava/util/Iterator;)Ljava/util/Enumeration;
- */
- JNIEXPORT jobject JNICALL Java_com_nedu_jni_helloword_HeaderFile_doInterface__Ljava_util_Iterator_2
- (JNIEnv *, jobject, jobject);
- /*
- * Class: com_nedu_jni_helloword_HeaderFile
- * Method: doStudent
- * Signature: (Lcom/nedu/jni/helloword/Student;)Lcom/nedu/jni/helloword/Student;
- */
- JNIEXPORT jobject JNICALL Java_com_nedu_jni_helloword_HeaderFile_doStudent__Lcom_nedu_jni_helloword_Student_2
- (JNIEnv *, jobject, jobject);
- /*
- * Class: com_nedu_jni_helloword_HeaderFile
- * Method: doString
- * Signature: ([Ljava/lang/String;)[Ljava/lang/String;
- */
- JNIEXPORT jobjectArray JNICALL Java_com_nedu_jni_helloword_HeaderFile_doString___3Ljava_lang_String_2
- (JNIEnv *, jobject, jobjectArray);
- /*
- * Class: com_nedu_jni_helloword_HeaderFile
- * Method: doObjects
- * Signature: ([Ljava/lang/Object;)[Ljava/lang/Object;
- */
- JNIEXPORT jobjectArray JNICALL Java_com_nedu_jni_helloword_HeaderFile_doObjects
- (JNIEnv *, jobject, jobjectArray);
- /*
- * Class: com_nedu_jni_helloword_HeaderFile
- * Method: doInterface
- * Signature: ([Ljava/util/Iterator;)[Ljava/util/Enumeration;
- */
- JNIEXPORT jobjectArray JNICALL Java_com_nedu_jni_helloword_HeaderFile_doInterface___3Ljava_util_Iterator_2
- (JNIEnv *, jobject, jobjectArray);
- /*
- * Class: com_nedu_jni_helloword_HeaderFile
- * Method: doStudent
- * Signature: ([Lcom/nedu/jni/helloword/Student;)[Lcom/nedu/jni/helloword/Student;
- */
- JNIEXPORT jobjectArray JNICALL Java_com_nedu_jni_helloword_HeaderFile_doStudent___3Lcom_nedu_jni_helloword_Student_2
- (JNIEnv *, jobject, jobjectArray);
- /*
- * Class: com_nedu_jni_helloword_HeaderFile
- * Method: doAll
- * Signature: ([I[Ljava/lang/String;[Lcom/nedu/jni/helloword/Student;)Ljava/lang/Object;
- */
- JNIEXPORT jobject JNICALL Java_com_nedu_jni_helloword_HeaderFile_doAll
- (JNIEnv *, jclass, jintArray, jobjectArray, jobjectArray);
- #ifdef __cplusplus
- }
- #endif
- #endif
三、頭文件分析如下:1、文件的前九行就不用說了,他們是是C、C++的頭,應該很好理解。
2、方法的註釋部分,每個方法都有它的註釋部分,這些都是相似的,對其中一個分析:
http://blog.csdn.net/xyz_lmn/article/details/6966259
- /*
- * Class: com_nedu_jni_helloword_HeaderFile
- * Method: doVoid
- * Signature: ()V
- */
註釋部分分爲三部分Class、Method、Signature。
Class:表示Native方法的類名稱。
Method:表示方法名稱
Signature:是方法的標識,它是一個標識符,主要供我們在JNI操作java對象的方法使用的。
Signature一般是兩部分構成,一個方法的參數,另一個是返回類型。方法參數在括號裏面,返回類型在後面,
例如
- ()V 返回爲void,沒有參數。
- (DFZ[C)I 返回爲int,參數爲double、float、char[]
- (Ljava/lang/String;)Ljava/lang/String;返回String,參數爲String
如果不清楚其中的字符含義,就不能知道其中的意思,其中字符對應有基本類型、對象類型、數組類型。分析如下1)基本類型的對應關係如下:
2) 方法參數或者返回值爲java中的對象時,必須以“L”加上其路徑,不過此路徑必須以“/”分開,自定義的對象也使用本規則,不在包中時直接“L”加上類名稱。比如說java.lang.String爲“java/lang/String”,com.nedu.jni.helloword.Student爲"com/nedu/jni/helloword/Student"
3)方法參數或者返回值爲數組時類型前加上[,例如[I表示int[],[[[D表示 double[][][],即幾維數組就加幾個[。
看一下例子:
3、方法的聲明
- JNIEXPORT void JNICALL Java_com_nedu_jni_helloword_HeaderFile_doArray(JNIEnv *,jobject,jobjectArray);
從上面的頭文件可以看出方法基本有7部分組成。
1、3部分是都是JNI的關鍵字,表示此函數是要被JNI調用的。
2、表示方法的返回類型
4、爲JNI中標識此方法來源於java的標識頭
5、方法所在類的包名+類名
6、方法名
7、參數,它們有一個共同的特點,包含JNIEnv *――它是一個接口指針,用於定位函數表中的函數!
在JNI規範中一般稱 爲 “Interface Pointer”。看到這兒好像和過程調用很類似了!是的,JNI中
的操作過程,就是面向過程的!後面的jobject是 一個指向該類的指針,類似與C語言中的this。這個
第二個參數是變化的,當該方法爲類的實例方法時該參數爲jobject;當該方法爲類方法(即靜態方法)
時該參數爲jclass,指向該類的class。
根據不同方法前綴生成的頭文件比較如下:
1、static與非static的比較:
- /*
- * Class: com_nedu_jni_helloword_HeaderFile
- * Method: doInt
- * Signature: (DD)I
- */
- JNIEXPORT jint JNICALL Java_com_nedu_jni_helloword_HeaderFile_doInt__DD
- (JNIEnv *, <span style="background-color: rgb(255, 0, 0);">jobject</span>, jdouble, jdouble);
- /*
- * Class: com_nedu_jni_helloword_HeaderFile
- * Method: doInt
- * Signature: (DDD)I
- */
- JNIEXPORT jint JNICALL Java_com_nedu_jni_helloword_HeaderFile_doInt__DDD
- (JNIEnv *, <span style="color:#000000;background-color: rgb(255, 0, 0);">jclass</span>, jdouble, jdouble, jdouble);
第一個是非static方法,第二個是static方法,不同點如上紅色標記。其中的不同將在以後提到。
2、 private、friendly、protected以及public這些方法限制符不會在JNI的頭文件中出現。這些訪問修飾符只有在其它類
使用這些方法時有效!JNI中不關心此修飾符!
Android.mk文件是在使用NDK編譯C代碼時必須的文件,Android.mk文件中描述了哪些C文件將被編譯且指明瞭如何編譯。掌握Android.mk文件的編寫主要是掌握其裏頭將要使用的一些關鍵字,先來看一個簡單的例子,這個例子使用的是android NDK帶的
HellJni的例子。
- LOCAL_PATH := $(call my-dir)
- include $(CLEAR_VARS)
- LOCAL_MODULE := hello-jni
- LOCAL_SRC_FILES := hello-jni.c
- include $(BUILD_SHARED_LIBRARY)
LOCAL_PATH 是描述所有要編譯的C文件所在的根目錄,這邊的賦值爲$(call my-dir),代表根目錄即爲Android.mk所在的目錄。
include $(CLEAR_VARS) 代表在使用NDK編譯工具時對編譯環境中所用到的全局變量清零,如LOCAL_MODULE,LOCAL_SRC_FILES等,因爲在一次NDK編譯過程中可能會多次調用Android.mk文件,中間用到的全局變量可能是變化的。關於這個問題看了下面比較複雜的例子可能就明白了。
LOCAL_MODULE 是最後生成庫時的名字的一部分,給其加上前綴lib和後綴.so就是生成的共享庫的名字libhello-jni.so。
LOCAL_SRC_FILES 指明要被編譯的c文件的文件名
include $(BUILD_SHARED_LIBRARY) 指明NDK編譯時將生成一些共享庫
參考:
android編譯系統makefile(Android.mk)寫法
android makefile(android.mk)分析(序)
<!-- JiaThis Button BEGIN -->
<div id="ckepop">
<a href="http://www.jiathis.com/share" class="jiathis jiathis_txt" target="_blank"><img src="http://v2.jiathis.com/code_mini/images/btn/v1/jiathis1.gif" border="0" /></a>
<a class="jiathis_counter_style_margin:3px 0 0 2px"></a>
</div>
<script type="text/javascript" src="http://v2.jiathis.com/code_mini/jia.js" charset="utf-8"></script>
<!-- JiaThis Button END --><!-- JiaThis Button BEGIN -->
<div id="ckepop">
<a href="http://www.jiathis.com/share" class="jiathis jiathis_txt" target="_blank"><img src="http://v2.jiathis.com/code_mini/images/btn/v1/jiathis1.gif" border="0" /></a>
<a class="jiathis_counter_style_margin:3px 0 0 2px"></a>
</div>
<script type="text/javascript" src="http://v2.jiathis.com/code_mini/jia.js" charset="utf-8"></script>
<!-- JiaThis Button END -->
參考網址:
http://blog.csdn.net/xyz_lmn/article/details/6959545
Android JNI入門第三篇——jni頭文件分析
http://blog.csdn.net/xyz_lmn/article/details/6966259
Android JNI入門第四篇——Android.mk文件分析
http://blog.csdn.net/xyz_lmn/article/details/7017420
Android JNI開發提高篇
http://blog.csdn.net/xyz_lmn/article/details/6956003
Android JNI入門第二篇——Java參數類型與本地參數類型對照
http://wenku.baidu.com/view/e9e28ca1b0717fd5360cdc18.html
JNI入門
http://www.ibm.com/developerworks/cn/java/j-jni/
使用 Java Native Interface 的最佳實踐
http://helloxuweifu.iteye.com/blog/1168647
http://blog.csdn.net/kangyaping/article/details/6584027
JNI函數調用大全
http://newfaction.net/2010/11/30/java-jni-getfieldid-and-getmethodid-and-parameter-description.html
java jni GetFieldID 和 GetMethodID 以及參數的說明
http://hi.baidu.com/spmno/blog/item/7d4d764ea78a6809b3de0588.html
jni中使用數組的幾個方法
http://xxw8393.blog.163.com/blog/static/3725683420107109411366/
JNI 返回結構體參數
http://www.cnblogs.com/nicholas_f/archive/2010/11/30/1892124.html
JNI中java類型與C/C++類型對應關係
http://blog.csdn.net/sunny09290/article/details/6884994
JNI數據類型
http://www.cnblogs.com/liangwind/archive/2009/08/18/1925515.html
jni --c/c++ 數據類型、數組、對象
http://www.cnblogs.com/diyunpeng/archive/2009/09/24/1573296.html
Java有符號數與無符號數
http://www.stuhack.com/biancheng/java/35169.html
Java的基本數據類型是無符號的