Android JNI和NDK學習(08)--JNI實例一 傳遞基本類型數據
本文介紹在Java和JNI之間相互傳遞基本數據類型的方法。
由於前面已經詳細介紹搭建和建立NDK工程的完整流程(參考“靜態實現流程”或“動態實現流程”),這里就不再介紹流程;而是將重點放在說明如何實現Java和JNI之間相互傳遞基本數據。
1 建立eclipse工程
建立工程NdkBasicParam,NdkBasicParam.java的內容如下:
package com.skywang.ndk; import android.app.Activity; import android.os.Bundle; import android.widget.TextView; import android.util.Log; public class NdkBasicParam extends Activity { private static final String TAG="sky--NdkBasicParam"; private int ival; private float fval; private String str; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Log.d(TAG, "onCreate"); // 獲取從jni傳來的整數 ival = intFromJni(); Log.d(TAG, "ival="+ival); // 將整數傳遞到jni intToJni((int)123); // 獲取從jni傳來的float fval = floatFromJni(); Log.d(TAG, "fval="+fval); // 將float傳遞到jni floatToJni((float)456.78); // 獲取從jni傳來的string str = stringFromJni(); Log.d(TAG, "str="+str); // 將string傳遞到jni stringToJni("Hello From Java"); } // jni中注冊的方法 private native int intFromJni(); private native void intToJni(int val); private native float floatFromJni(); private native void floatToJni(float val); private native String stringFromJni(); private native void stringToJni(String val); static { // 加載本地libndk_load.so庫文件 System.loadLibrary("ndk_basic_param"); } }
NdkBasicParam.java的內容很簡單,主要就是調用一些JNI中注冊的本地方法。
2 實現JNI
(01) 在工程的jni目錄下新建ndk_basic_param.c
ndk_basic_param.c的代碼如下:
#include <stdlib.h> #include <string.h> #include <stdio.h> #include <jni.h> #include <assert.h> // 獲取數組的大小 # define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0]))) // 指定要注冊的類,對應完整的java類名 #define JNIREG_CLASS "com/skywang/ndk/NdkBasicParam" // 引入log頭文件 #include <android/log.h> // log標簽 #define TAG "ndk_basic_param" // 定義info信息 #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG,__VA_ARGS__) // 定義debug信息 #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__) // 定義error信息 #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG,__VA_ARGS__) /* * 將字符串由JNI傳遞給Java * 參數說明: * env : JNI 接口指針。 * claszz : Java 類對象。 */ JNIEXPORT jstring JNICALL stringFromJni(JNIEnv *env, jobject clazz) { // 將“Hello From jni”轉行成jstring類型 jstring str = (*env)->NewStringUTF(env, "Hello From Jni"); return str; } /* * 將字符串從java從到jni層。 * 參數說明: * env : JNI 接口指針。 * claszz : Java 類對象。 * val : java傳遞給jni的string類型值。 */ JNIEXPORT void JNICALL stringToJni(JNIEnv *env, jobject clazz, jstring val) { // 將java傳遞給jni的string轉行成char *類型。 // const char * GetStringUTFChars(JNIEnv *env, jstring string, jboolean *isCopy); // env:JNI 接口指針。 // string:Java 字符串對象。 // isCopy:指向布爾值的指針,JNI_TRUE或JNI_FALSE。 // JNI_TRUE —— 開新內存,然后把java中的string拷貝到這個內存中,然后返回指向這個內存地址的指針。 // JNI_FALSE —— 直接返回指向java中String內存的指針,這個時候千萬不要改變這個內存的內容,這將破壞String在java中始終是常量這個原則。 char *str = (char *)(*env)->GetStringUTFChars(env, val, JNI_FALSE); LOGD("%s str=%s\n", __func__, str); } /* * 將“浮點數”由JNI傳遞給Java * 參數說明: * env : JNI 接口指針。 * claszz : Java 類對象。 */ JNIEXPORT jfloat JNICALL floatFromJni(JNIEnv *env, jobject clazz) { return (jfloat)1.34; } /* * 將“浮點數”從Java從到JNI層。 * 參數說明: * env : JNI 接口指針。 * claszz : Java 類對象。 * val : java傳遞給jni的浮點數。 */ JNIEXPORT void JNICALL floatToJni(JNIEnv *env, jobject clazz, jfloat val) { float f = (float)val; LOGD("%s f=%f\n", __func__, f); } /* * 將“整數”由JNI傳遞給Java * 參數說明: * env : JNI 接口指針。 * claszz : Java 類對象。 */ JNIEXPORT jint JNICALL intFromJni(JNIEnv *env, jobject clazz) { return (jint)25; } /* * 將“整數”從Java從到JNI層。 * 參數說明: * env : JNI 接口指針。 * claszz : Java 類對象。 * val : java傳遞給jni的整數。 */ JNIEXPORT void JNICALL intToJni(JNIEnv *env, jobject clazz, jint val) { int i = (int)val; LOGD("%s i=%d\n", __func__, i); } // Java和JNI函數的綁定表 static JNINativeMethod method_table[] = { { "intFromJni" , "()I" , (void*)intFromJni }, { "intToJni" , "(I)V" , (void*)intToJni }, { "floatFromJni" , "()F" , (void*)floatFromJni }, { "floatToJni" , "(F)V" , (void*)floatToJni }, { "stringFromJni" , "()Ljava/lang/String;" , (void*)stringFromJni }, { "stringToJni" , "(Ljava/lang/String;)V", (void*)stringToJni }, }; // 注冊native方法到java中 static int registerNativeMethods(JNIEnv* env, const char* className, JNINativeMethod* gMethods, int numMethods) { jclass clazz; clazz = (*env)->FindClass(env, className); if (clazz == NULL) { return JNI_FALSE; } if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) { return JNI_FALSE; } return JNI_TRUE; } int register_basic_ndk_param(JNIEnv *env) { // 調用注冊方法 return registerNativeMethods(env, JNIREG_CLASS, method_table, NELEM(method_table)); } // JNI_OnLoad在jni注冊時,會被回調執行。 JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { JNIEnv* env = NULL; jint result = -1; if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) { return result; } register_basic_ndk_param(env); // 返回jni的版本 return JNI_VERSION_1_4; }
JNI_OnLoad()會在JNI注冊時被回調。 JNI_OnLoad()調用register_basic_ndk_param(),而register_basic_ndk_param()調用registerNativeMethods();在registerNativeMethods()中將method_table注冊到Java中。然后就可以通過Java調用注冊的函數。
(02) 實現Android.mk
在工程的jni目錄下新建Android.mk,Android.mk的代碼如下:
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := ndk_basic_param LOCAL_SRC_FILES := ndk_basic_param.c # 添加對log庫的支持 LOCAL_LDLIBS:=-L$(SYSROOT)/usr/lib -llog # 若生成static的.a,只需添加 LOCAL_LDLIBS:=-llog include $(BUILD_SHARED_LIBRARY) LOCAL_PATH := $(call my-dir)
3 運行工程
打印信息如下:
D/sky--NdkBasicParam: onCreate
D/sky--NdkBasicParam: ival=25
D/ndk_basic_param: intToJni i=123
D/sky--NdkBasicParam: fval=1.34
D/ndk_basic_param: floatToJni f=456.779999
D/sky--NdkBasicParam: str=Hello From Jni
D/ndk_basic_param: stringToJni str=Hello From Java
點擊下載:"源代碼"