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
点击下载:"源代码"