没有完全原创,参考下面链接的大部分内容:
http://blog.csdn.net/zhenyongyuan123/article/details/5862054
http://blog.sina.com.cn/s/blog_7a2ffd5c01013vrv.html
本文仅作学习使用。
实现JNI中本地函数注册有两种方式:
1.采用默认的本地函数注册流程。
2.自己重写JNI_OnLload()函数。
当Android的VM执行到C组件(*so)里的System.loadLibrary()函数时,首先会去执行C组件里的JNI_OnLoad()函数,其用途有二
一、告诉java VM此C组件使用哪一个JNI版本。
如果你没有提供JNI_OnLoad()函数,VM会默认使用最老得JNI1.1版本。
二、可以藉由JNI_OnLoad()来获取JNIEnv.JNIEnv代表java环境,通过JNIEnv*指针就可以对java端的代码进行操作。
如(可参考android_media_MediaPlayer.cpp):
jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
JNIEnv* env = NULL;
jint result = -1;
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
ALOGE("ERROR: GetEnv failed\n");
goto bail;
}
assert(env != NULL);
......
if (register_android_mtp_MtpDatabase(env) < 0) {
ALOGE("ERROR: MtpDatabase native registration failed");
goto bail;
}
......
/* success -- return valid version number */
result = JNI_VERSION_1_4;
bail:
return result;
}
实例一:C++调用java中的方法
在上面onLoad()的基础上,我们开始调用java的东西(参考android_mtp_MtpDatabase.cpp)
a.获取对象的类的id
jclass classID = pEnv->FindClass(className);
如:
在android_mtp_MtpDatabase.cpp的register_adnroid_mtp_MtpDatabase(JNIEnv *env)中
clazz = env->FindClass("android/mtp/MtpDatabase");
b.获取要调用的方法id,包括静态和普通方法。
jmethodID methodID = pEnv->GetStaticMethodID(classID, methodName, paramCode);
jmethodID methodID = pEnv->GetMethodID(classID,methodName, paramCode);
如:
method_beginSendObject = env->GetMethodID(clazz, "beginSendObject", "(Ljava/lang/String;IIIJJ)I");
这其中的最后一个参数代表方法的参数信息,因为java支持多态,只有完整的参数信息才可以找到唯一的方法。
这个参数有着特定的格式,见附录。
c.调用方法,同样包括静态和普通。
pEnv ->CallStatic***Method(classID,methodID);
pEnv ->Call***Method(classID,methodID);
不同的返回参数,调用不同的方法,如CallBooleanMethod,CallIntMethod,CallObjectMethod,CallStaticIntMethod等等。
如:
MtpObjectHandle result = env->CallIntMethod(mDatabase, method_beginSendObject,
pathStr, (jint)format, (jint)parent, (jint)storage,
(jlong)size, (jlong)modified);
附录:
在GetMethodID最后一个参数是签名字符串,用来标示java函数和成员的唯一性。因为java中存在重载函数,所以一个函数名不足以唯一指定一个函数,这时候就需要签名字符串来指定函数的参数列表和返回值类型了。
函数签名是一个字符串:"(M)N",括号中的内容是函数的参数类型,括号后面表示函数的返回值。
具体的每一个字符的对应关系如下
字符 Java类型 C类型
V void void
Z jboolean boolean
I jint int
J jlong long
D jdouble double
F jfloat float
B jbyte byte
C jchar char
S jshort short
数组则以”["开始,用两个字符表示
[I jintArray int[]
[F jfloatArray float[]
[B jbyteArray byte[]
[C jcharArray char[]
[S jshortArray short[]
[D jdoubleArray double[]
[J jlongArray long[]
[Z jbooleanArray boolean[]
如果Java函数的参数是class,则以”L”开头,以”;”结尾,中间是用”/” 隔开的包及类名。而其对应的C函数名的参数则为jobject
一个例外是String类,其对应的类为jstring
Ljava/lang/String; String jstring
Ljava/net/Socket; Socket jobject
实例二:java调用C++中的方法
Java端代码:
package com.jni;
public class JavaHello {
public static native String hello();
static {
// load library: libtest.so
try {
System.loadLibrary("test");
} catch (UnsatisfiedLinkError ule) {
System.err.println("WARNING: Could not load library!");
}
}
public static void main(String[] args) {
String s = new JavaHello().hello();
System.out.println(s);
}
}
本地C语言代码:
#include
#include
#include
#include
#include
JNIEXPORT jstring JNICALL native_hello(JNIEnv *env, jclass clazz)
{
printf("hello in c native code./n");
return (*env)->NewStringUTF(env, "hello world returned.");
}
#define JNIREG_CLASS "com/jni/JavaHello"//指定要注册的类
/**
* Table of methods associated with a single class.
*/
static JNINativeMethod gMethods[] = {
{ "hello", "()Ljava/lang/String;", (void*)native_hello },//绑定
};
/*
* Register several native methods for one class.
*/
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;
}
/*
* Register native methods for all classes we know about.
*/
static int registerNatives(JNIEnv* env)
{
if (!registerNativeMethods(env, JNIREG_CLASS, gMethods,
sizeof(gMethods) / sizeof(gMethods[0])))
return JNI_FALSE;
return JNI_TRUE;
}
/*
* Set some test stuff up.
*
* Returns the JNI version on success, -1 on failure.
*/
JNIEXPORT jint JNICALL 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 -1;
}
assert(env != NULL);
if (!registerNatives(env)) {//注册
return -1;
}
/* success -- return valid version number */
result = JNI_VERSION_1_4;
return result;
}
编译及运行流程:
1 设置三个环境变量:
export JAVA_HOME:=/usr/lib/jvm/java-6-sun-1.6.0.15
export JAVA_SRC_PATH:=/home/kortide/Jackey/jni/jni_onload/com/jfo
export NATIVE_SRC_PATH:=/home/kortide/Jackey/jni/jni_onload/jni
2 编译JavaHello.java:
javac $JAVA_SRC_PATH/JavaHello.java
3. 编译NativeHello.c,生成共享库
gcc -I $JAVA_HOME/include -I $JAVA_HOME/include/linux -c -o $NATIVE_SRC_PATH/NativeHello.o $NATIVE_SRC_PATH/NativeHello.c
gcc -fPIC -I $JAVA_HOME/include -I $JAVA_HOME/include/linux -shared -o $NATIVE_SRC_PATH/libtest.so $NATIVE_SRC_PATH/NativeHello.o
4. 运行
java com/jni/JavaHello