昨天學習windows上的JNI編程,JNI說白了就是java和c語言的一個互相溝通的橋樑,java可以調用JNI來完成調用C語言實現的方法。JNI的全稱是(Java native interface),其實在編程重你只需要將與java交互的函數寫出來,其他的C語言內部調用的就可以直接使用C語言相關語法了。閒話少說,開始正題吧。
要想在windroid或者是linux上使用JNI必須要下載NDK的並且指定路徑,在windows我們還需要安裝一個sygwin,這裏我就不再說怎樣安裝cygwin了,在你安裝好的cygwin文件夾中找到一個etc的文件夾,在這個文件中找到一個profile文件,修改其中的Path後加上:(ndk的路徑),在我理解ndk就是構建出了一個重pc到android的一個交叉編譯環境,當然它裏面還有很多我不知道的,還有待探索。然後我們就可以開始使用了,當然你要是用eclipse寫C/C++還需要安裝cdt插件。安裝過後就可以使用eclipse編寫c/c++的代碼了。
下面我們來看看代碼怎樣編寫吧,首先我們在android工程中創建一個jni的文件夾,在jni的文件夾中創建c語言的源文件,在android中穿件一個類,類中可以使用native標識創建函數例如下面:
public class DataProvider {
//帶參數的c語言調用java語言
public int add2( int x, int y){
return x + y;
}
//無參數的函數C語言調用java語言
public void show(){
System.out.println( "我被調用了啊" );
}
//無參數的靜態函數C語言調用java語言
public static void show2(){
System.out.println( "我又被調用了啊" );
}
//將函數使用native標識,可以自動生成相應的函數
public native int add(int x, int y);
public native int sayHello( String hello );
public native int[] array(int[] arr);
public native int callbackadd2();
public native void callbackshow();
public native static void callbackshow2();
public native void callbackshow3();
}
將這個類寫好了就可以使用javah (類的全類名), 全類名是指包名+類名,例如com.example.testjni.DataProvider,這樣就可以生成一個c語言中使用的頭文件例如:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_testjni_DataProvider */
#ifndef _Included_com_example_testjni_DataProvider
#define _Included_com_example_testjni_DataProvider
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_example_testjni_DataProvider
* Method: add
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_example_testjni_DataProvider_add
(JNIEnv *, jobject, jint, jint);
/*
* Class: com_example_testjni_DataProvider
* Method: sayHello
* Signature: (Ljava/lang/String;)I
*/
JNIEXPORT jint JNICALL Java_com_example_testjni_DataProvider_sayHello
(JNIEnv *, jobject, jstring);
/*
* Class: com_example_testjni_DataProvider
* Method: array
* Signature: ([I)[I
*/
JNIEXPORT jintArray JNICALL Java_com_example_testjni_DataProvider_array
(JNIEnv *, jobject, jintArray);
/*
* Class: com_example_testjni_DataProvider
* Method: callbackadd2
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_com_example_testjni_DataProvider_callbackadd2
(JNIEnv *, jobject);
/*
* Class: com_example_testjni_DataProvider
* Method: callbackshow
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_example_testjni_DataProvider_callbackshow
(JNIEnv *, jobject);
/*
* Class: com_example_testjni_DataProvider
* Method: callbackshow2
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_example_testjni_DataProvider_callbackshow2
(JNIEnv *env, jclass);
/*
* Class: com_example_testjni_DataProvider
* Method: callbackshow3
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_example_testjni_DataProvider_callbackshow3
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
這些都是使用javah後自動生成的。下一步我將該在c語言中是實現這些函數了,在此之前還需要創建一個mk文件,這個文件就是makefile,C語言在編譯成庫的情況下就可以讀取makefile來編譯。
mk文件:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
#對應的c語言的函數庫
LOCAL_MODULE := hello
#對應c代碼的文件
LOCAL_SRC_FILES := hello.c functions.c
LOCAL_LDLIBS := -llog//使用本地庫
include $(BUILD_SHARED_LIBRARY)
#include <stdio.h>
#include "com_example_testjni_DataProvider.h" //引用頭文件
#include <android/log.h> //打印的日誌
const char *TAG = "clog";
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__ );
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__ );
//使用log頭文件中的函數打印日誌到eclipse中的logcat,在這裏需要在mk文件中添加庫引用,LOCAL_LDLIBS += -llog
JNIEXPORT jint JNICALL Java_com_example_testjni_DataProvider_add
(JNIEnv *env, jobject o, jint x, jint y)
{
LOGI("%d\n", x );
LOGD("%d\n", y );
return x + y; //直接返回就可以
}
//int print(jintArray localarray, int i);
int print( int* localarray, int i )
{
LOGD( "array = %d\n", *(localarray+i));
return 0;
}//平常的c語言代碼,可以直接調用
/*
* Class: com_example_testjni_DataProvider
* Method: sayHello
* Signature: (Ljava/lang/String;)I
*/
JNIEXPORT jint JNICALL Java_com_example_testjni_DataProvider_sayHello
(JNIEnv *env, jobject o, jstring hello)
{
}
/*
* Class: com_example_testjni_DataProvider
* Method: array
* Signature: ([I)[I
*/
JNIEXPORT jintArray JNICALL Java_com_example_testjni_DataProvider_array
(JNIEnv *env, jobject obj, jintArray array )//傳的是一個java中int型數組,java調用c語言
{
//獲得數組長度
int length = (*env)->GetArrayLength(env, array);
int i;
jint* localarray = (*env)->GetIntArrayElements(env, array, 0);
for( i = 0; i < length; i++ ){
*(localarray+i) += 5;
print( localarray, i );
}
return array;
}
/*
* Class: com_example_testjni_DataProvider
* Method: callbackadd2
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_com_example_testjni_DataProvider_callbackadd2
(JNIEnv *env, jobject obj)
{
jclass clazz = (*env)->FindClass(env, "com/example/testjni/DataProvider");
jmethodID mid = (*env)->GetMethodID(env, clazz, "add2", "(II)I");
return (*env)->CallIntMethod(env, obj, mid, 3, 5);
}
/*
* Class: com_example_testjni_DataProvider
* Method: callbackshow
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_example_testjni_DataProvider_callbackshow
(JNIEnv *env, jobject obj)
{
jclass clazz = (*env)->FindClass(env, "com/example/testjni/DataProvider");
jmethodID mid = (*env)->GetMethodID(env, clazz, "show", "()V");//
(*env)->CallVoidMethod(env, obj, mid);
}
/*
* Class: com_example_testjni_DataProvider
* Method: callbackshow2
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_example_testjni_DataProvider_callbackshow2
(JNIEnv *env, jclass jc)
{
jmethodID mid = (*env)->GetStaticMethodID(env, jc, "show2", "()V");
jobject obj = (*env)->CallStaticObjectMethod(env, jc, mid);
(*env)->CallVoidMethod(env, obj, mid);
}
/*
* Class: com_example_testjni_DataProvider
* Method: callbackshow3
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_example_testjni_DataProvider_callbackshow3
(JNIEnv *env, jobject obj)
{
jclass clazz = (*env)->FindClass(env, "com/example/testjni/DataProvider");
jmethodID mid = (*env)->GetStaticMethodID(env, clazz, "show2", "()V");
(*env)->CallVoidMethod(env, obj, mid);
}
java調用C語言將java傳過來的值,使用jni的方法進行處理,然後使用,返回,c語言調用java需要在C語言代碼中進行映射,例如:
JNIEXPORT void JNICALL Java_com_example_testjni_DataProvider_callbackshow3
(JNIEnv *env, jobject obj)
{
jclass clazz = (*env)->FindClass(env, "com/example/testjni/DataProvider");//得到類的字節碼
jmethodID mid = (*env)->GetStaticMethodID(env, clazz, "show2", "()V");//得到函數的id
(*env)->CallVoidMethod(env, obj, mid);//執行函數
}
執行函數有不同的call函數,例如返回值是int是CallIntMethod(env, obj, mid, 3, 5);空類型是CallVoidMethod(env, obj, mid);後面的()V是函數簽名,代表是返回值是void型,無參的函數。(II)I返回值是int型參數是兩個int值得函數。c語言編寫後要在工程的src目錄下運行ndk-bulid命令就可以生成一個c語言庫,在eclipse工程目錄中也有顯示。
在每次編譯之前需要刪除obj文件夾,以清除緩存。
jni對於一些可以很大的提高java代碼的隱祕性,而且使用c語言開發jni程序可以提高效率。