android JNI_OnLoad()函数

没有完全原创,参考下面链接的大部分内容:

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

 

你可能感兴趣的:(android)