android windows 上JNI編程

    昨天學習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程序可以提高效率。
發佈了36 篇原創文章 · 獲贊 8 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章