最近在看关于android JNI相关的东西 里面一直出现一个名词 原生函数 请问什么叫做原生函数呢,请哪位高手指点下 谢谢了
------解决方案--------------------
这里的原生函数应该是指C/C++开发好的dll中的函数,属于C/C++函数。Java使用JNI,其实就是变相的调用C/C++里的方法。这个方法就叫原生函数。好啰嗦。。- -
------解决方案--------------------
没听说过,不好意思。
------解决方案--------------------
系统环境本身自带的东西就叫做原生的.
比如Java中的各种List实现,各种Map等等都叫做原生
------解决方案--------------------
JNI,全称Java Native Interface,是用于让运行在JVM中的Java代码和运行在JVM外的Native代码(主要是C或者C++)沟通的桥梁。代码编写者即可以使用JNI从Java的程序中调用Native代码,又可以从Native程序中调用Java代码。这样,编程人员可以将低阶的代码逻辑包装到高阶的程序框架中,获得高性能高效率的同时保证了代码框架的高抽象性。
在Android中,仅有以下类库是允许在JNI中使用的:
libc (C library) headers
libm (math library) headers
JNI interface headers
libz (Zlib compression) headers
liblog (Android logging) header
OpenGL ES 1.1 (3D graphics library) headers (since 1.6)
A Minimal set of headers for C++ support
JNI本身仅仅是一个把两者融合的工具,作为编程者需要做的,就是在Java代码和Native代码中按照固定的格式告诉JNI如何调用对方。在Android中,有两种方式可以调用JNI,一种是Google release的专门针对Android Native开发的工具包,叫做NDK。去Android网站上下载该工具包后,就可以通过阅读里面的文档来setup一个新的包含Native代码的工程,创建自己的Android.mk文件,编译等等;另一种是完整的源码编译环境 ,也就是通过git从官方网站获取完全的Android源代码平台。这个平台中提供有基于make的编译系统。更多细节请参考这里。不管选择以上两种方法的哪一个,都必须编写自己的Android.mk文件,有关该文件的编写请参考相关文档。
下面通过一个简单的使用例子来讲解JNI。Android给C和C++提供的是两套不同的Native API,本文仅以C++举例说明。假设这么一个需求,Java代码需要打印一个字符串,而该字符串需要Native代码计算生成。对应的JNI流程是这样的:
1. 在准备打印字符串的Android类中,添加两段代码。
第一段是:
private native String getPrintStr();
这一行代码的目的是告诉JNI,这个Java文件中有这么一个函数,该函数是在Native代码中执行的,Native代码会返回一个字符串供Java代码来输出。
第二段是:
try {System.loadLibrary(“LIBNAME” }
catch (UnsatisfiedLinkError ule) {Log.e(TAG, “Could not load native library”);}
这两行代码是告诉JNI,你需要找的所有Native函数都在libLIBNAME.so这个动态库中。注意JNI会自动补全lib和so给LIBNAME,你只需要提供LIBNAME给loadLibrary就行了。在最后执行的时候,JNI会先找到这个动态库,然后找里面的OnLoad函数,具体注册流程由OnLoad函数接管。
关于如何确定这个LIBNAME,和如何定义OnLoad函数,下面就会讲。
2. 上面的第一步是告诉JNI,java代码需要和Native代码交互,同时把在哪里找,找什么都通知了。接下来的事情就由Native端接管。如果把上面的getPrintString函数申明比作原型,那么本地代码中的具体函数定义就应该和该原型匹配,JNI才能知道具体在哪里执行代码。具体来说,应该有一个对应的Native函数,有和Java中定义的函数同样的参数列表以及返回值。另外,还需要有某种机制让JNI将两者相互映射,方便参数和返回值的传递。在老版的JNI中,这是通过丑陋的命名匹配实现的,比如说在Java中定义的函数名是getPrintStr, 该函数属于package java.come.android.xxx,那么中对应Native代码中的函数名就应该是Java_com_android_xxx_getPrintStr。这样给开发人员带来了很多不便。可以用javah命令来生成对应Java code中定义函数的Native code版本header文件,从中得知传统的匹配方法是如何做的。具体过程如下:
通过SDK的方式编译Java代码。
找到Eclipse的工程目录,进入bin目录下。这里是编译出的java文件所对应的class文件所在。
假设包括Native函数调用的java文件属于com.android.xxx package,名字叫test.java,那么在bin下执行javah -jni com.android.xxx.test
执行完后,可以看到一个新生成的header文件,名字为com_android_xxx_test.h。打开后会发现已经有一个函数申明,函数名为java_com_android_xxx_test_getPrintStr。这个名字就包括了该函数所对应Java版本所在的包,文件以及名称。这就是JNI传统的确定名字的方法。
值得注意的是,header文件中不仅包含了基于函数名的映射信息,还包含了另一个重要信息,就是signature。一个函数的signature是一个字符串,描述了这个函数的参数和返回值。其中”()” 中的字符表示参数,后面的则代表返回值。例如”()V” 就表示void Func(); “(II)V” 表示 void Func(int, int); 数组则以”["开始,用两个字符表示。
具体的每一个字符的对应关系如下:
字符
Java类型
C类型
V
void
void
I
jint
int
Z
jboolean
boolean
J
jlong
long
D
jdouble
double
F
jfloat
float
B
jbyte
byte
C
jchar
char
S
jshort
short
上面的都是基本类型。如果Java函数的参数是class,则以"L"开头,以";"结尾,中间是用"/" 隔开的包及类名。而其对应的C函数名的参数则为jobject。 一个例外是String类,其对应的类为jstring。举例:
Ljava/lang/String; String jstring
Ljava/net/Socket; Socket jobject
如果JAVA函数位于一个嵌入类,则用$作为类名间的分隔符。例如 "(Ljava/lang/String;Landroid/os/FileUtils$FileStatus;)Z"
这个signature非常重要,是下面要介绍的新版命名匹配方法的关键点之一。所以,即使传统的命名匹配已经不再使用,javah这一步操作还是必须的,因为可以从中得到Java代码中需要Native执行的函数的签名,以供后面使用。
3. 在新版(版本号大于1.4)的JNI中,Android提供了另一个机制来解决命名匹配问题,那就是JNI_OnLoad。正如前面所述,每一次JNI执行Native代码,都是通过调用JNI_OnLoad实现的。下面的代码是针对本例的OnLoad代码:
/* Returns the JNI version on success, -1 on failure.
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* env = NULL;
jint result = -1;
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
LOGE("ERROR: GetEnv failed");
goto bail;
}
assert(env != NULL);
if (!register_Test(env)) {
LOGE("ERROR: Test native registration failed");
goto bail;
}
/* success -- return valid version number */
result = JNI_VERSION_1_4;
bail: return result;
}
分析这个函数。首先,OnLoad通过GetEnv函数获取JNI的环境对象,然后通过register_Test来注册Native函数。register_Test的实现如下:
int register_Test(JNIEnv *env) {
const char* const ClassPathName = "com/android/xxx/test";
return registerNativeMethods(env, ClassPathName, TestMethods,
sizeof(TestMethods) / sizeof(TestMethods[0]));
}
在这里,ClassPathName是Java类的全名,包括package的全名。只是用 “/” 代替 ”.” 。然后我们把类名以及TestMethods这个参数一同送到registerNativeMethods这个函数中注册。这个函数是基于JNI_OnLoad的命名匹配方式的重点。