不同编程语言之间的函数的关联

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011217649/article/details/54945411

一、JNI(Java Native Interface, Java本地调用)

作用:在Java程序中的函数可以调用C/C++编写的函数;

           在C/C++程序中的函数可以调用Java编写的函数;

JNI实例

Java(MediaScanner)<——>JNI(libmedia_jni.so)<——>Native(libmedia.so)

在Java层中的MediaScanner类中有一些函数需要由Native层来实现;

MediaScanner将通过JNI库libmedia_jni.so和Native层的libmedia.so交互;

从上面的分析可知,JNI层必须实现为动态库的形式,这样Java虚拟机才能加载和调用 它的函数;

Java层的MediaScanner分析

MediaScanner.java

public class MediaScanner
{
    static {
        /*1)加载对应的JNI库,media_jni是JNI库的名字,在实际加载动态库的时候将其拓展成libmedia_jni.so,windows平台上拓展为media_jni.dll*/
        System.loadLibrary("media_jni");
        native_init();  //调用native_init函数
    }
    ......
    //2)声明一个native函数,native为Java关键字,表示它将由JNI层完成。
    private static native final void native_init();
    ......
    private native void processFile(String path, String mimeType, MediaScannerClient client);
    ......
}

上面的代码列出了两个要点:一个是加载JNI库,另一个是Java的native函数。

加载JNI库

原则上在调用native函数前,都可以加载;通常在类的static语句中加载;

调用System.loadLibrary()方法;

从上面的代码可以发现,native_init和processFile函数前都有Java的关键字native,它表示这两个函数将由JNI层来实现;

JNI层MediaScanner的分析

MediaScanner的JNI层代码在android_media_MediaScanner.cpp中

android_media_MediaScanner.cpp

//native_init的JNI实现
static void android_media_MediaScanner_native_init(JNIEnv *env)
{
    ......
}
//processFile的JNI层实现
static void android_media_MediaScanner_processFile(JNIEnv *env, jobject thiz, jstring path, jstring mimeType, jobject client)
{
    ......
}

Java层native_init函数如何对应JNI层的android_media_MediaScanner_native_init函数?

注册JNI函数

native_init函数位于android.media这个包中,它的全路径名应该是android.media.MediaScanner.native_init,而JNI层函数的名字是android_media_MediaScanner_native_init;

JNI层中把Java函数名称中的“.”换成“_”,通过这种方式native_init找到JNI层的对应函数android_media_MediaScanner_native_init;

1、静态方法

使用Java的工具程序javah找对应的JNI函数,流程如下:

先编写Java代码,然后编译生成.class文件。

使用Java工具程序javah,$ javah -o output packagename.classname

之后生成output.h的JNI层头文件,其中packagename.classname是Java代码编译后的class文件,在output.h中声明了对应的JNI层函数,只要实现里面的函数即可。

如MediaScanner对应JNI层头文件就是android_media_MediaScanner.h

对应关系:

当Java层调用native_init函数时,它会从对应的JNI库中寻找Java_android_media_MediaScanner_native_init函数,如果没有就报错。如果找到就为这两个函数建立一个关联关系,就是保存JNI层函数的函数指针,以后再调用就可以使用这个函数指针了,这些工作是由虚拟机完成的。

2、动态注册

JNI中使用JNINativeMethod结构保存上面Java和JNI层函数的一一对应的关系;

//定义一个JNINativeMethod数组,其成员就是Java层中所有的native函数的一一对应关系

static JNINativeMethod gMethods[] = {
    ......
    {
        "processFile"  //Java中native函数的函数名
        //processFile的签名信息,签名信息由函数参数及返回值构成;
        "(Ljava/lang/String;Ljava/lang/String;Landroid/media/MediaScannerClient;)V",
        (void *)android_media_MediaScanner_processFile  //JNI层对应的函数指针
    },
    ......
    {
        "native_init",
        "()V",
        (void *)android_media_MediaScanner_native_init
    },
};
//注册JNINativeMethod数组
int register_android_media_MediaScanner(JNIEnv *env)
{
    //调用AndroidRuntime的registerNativeMethods函数,第二个参数表明是Java中的哪个类
    return AndroidRuntime::registerNativeMethods(env, "andorid/media/MediaScanner", gMethods, NELEM(gMethods));
}

AndroidRunTime提供了一个registerNativeMethods函数来完成注册工作,下面看registerNativeMethods的实现,代码如下:

int AndroidRuntime::registerNativeMethods(JNIEnv* env, const char* className, const JNINativeMethod* gMethod, int numMethods)
{
    //调用jniRegisterNativeMethods函数完成注册
    return jniRegisterNativeMethods(env, className, gMethods, numMethods);
}
其中jniRegisterNativeMethods是Android平台为了方便JNI使用而提供的一个帮助函数,代码如下:

int jniRegisterNativeMethods(JNIEnv* env, const char* className, const JNINativeMethod* gMethods, int numMethods)
{
    jclass clazz;
    clazz = (*env)->FindClass(env, className);
    ......
    //实际上是调用JNIEnv的RegisterNatives函数完成注册的
    if((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {
        return -1;
    }
    return 0;
}

动态注册的工作只用两个函数就能完成。

jclass clazz = (*env)->FindClass(env, className);
(*env)->RegisterNatives(env, clazz, gMethods, numMethods);

动态注册的函数,当Java层通过System.loadLibrary加载完JNI动态库后,紧接着会查找该库中一个叫JNI_OnLoad的函数,调用它完成注册;

建议实现这个JNI_OnLoad函数,在其中做一些初始化的工作;

函数签名信息的生成

java提供了一个javap的工具可以帮助生成函数和变量的签名信息;

$ java -s -p xxx

其中xxx为编译生成的class文件,s表示输出内部数据类型的签名信息,p表示打印所有函数和成员的签名信息,默认只会答应public的成员和函数的签名信息;

二、XPCOM组件




阅读更多
换一批

没有更多推荐了,返回首页