1 什么是JNI
JNI是Java Native Interface的缩写,即Java本地接口.从Java1.1开始,JNI标准成为Java平台的一部分,它允许java代码和用其它语言编写的代码进行交互.JNI是本地编程接口,它使得在Java虚拟机(VM)内部运行的Java代码能够与用其他编程语言(如C,C++和汇编语言)的应用程序和库进行交互操作.
在Android中提供的JNI的方式,让Java程序可以调用C语言程序。Android中很多Java类都具有native接口,这些native接口就是同本地实现,然后注册到系统中的.
JNI在Android层次结构中的作用如下图所示:
在Android中,主要的JNI代码在以下的路径中:
Android源码根目录/frameworks/base/core/jni/
这个路径中的内容将被编译成库libandroid_runtime.so,这就是一个普通的动态库,被放置在目标系统的/system/lib目录中.
除此之外,Android还包含其他的JNI库,例如,媒体部分的JNI目录frameworks/base/media/jni/中,被编译成库libmedia_jni.so.
JNI中的各个文件实际上就是C++的普通文件,其命名一般和支持的Java类有对应关系。这种关系是习惯上的写法,而不是强制的。
在Android中实现的JNI库,需要连接动态库libnativehelper.so.
2 注册JNI方法
在Android源码根目录/frameworks/base/services/jni/目录下有一个onload.cpp文件,其内容如下:
19 |
#include "utils/Log.h" |
20 |
#include "utils/misc.h" |
23 |
int register_android_server_AlarmManagerService(JNIEnv* env); |
24 |
int register_android_server_BatteryService(JNIEnv* env); |
25 |
int register_android_server_InputApplicationHandle(JNIEnv* env); |
26 |
int register_android_server_InputWindowHandle(JNIEnv* env); |
27 |
int register_android_server_InputManager(JNIEnv* env); |
28 |
int register_android_server_LightsService(JNIEnv* env); |
29 |
int register_android_server_PowerManagerService(JNIEnv* env); |
30 |
int register_android_server_UsbDeviceManager(JNIEnv* env); |
31 |
int register_android_server_UsbHostManager(JNIEnv* env); |
32 |
int register_android_server_VibratorService(JNIEnv* env); |
33 |
int register_android_server_SystemServer(JNIEnv* env); |
34 |
int register_android_server_location_GpsLocationProvider(JNIEnv* env); |
35 |
int register_android_server_connectivity_Vpn(JNIEnv* env); |
36 |
int register_android_server_HelloService(JNIEnv *env); |
39 |
using namespace android; |
41 |
extern "C" jint JNI_OnLoad(JavaVM* vm, void * reserved) |
46 |
if (vm->GetEnv(( void **) &env, JNI_VERSION_1_4) != JNI_OK) { |
47 |
LOGE( "GetEnv failed!" ); |
50 |
LOG_ASSERT(env, "Could not retrieve the env!" ); |
52 |
register_android_server_PowerManagerService(env); |
53 |
register_android_server_InputApplicationHandle(env); |
54 |
register_android_server_InputWindowHandle(env); |
55 |
register_android_server_InputManager(env); |
56 |
register_android_server_LightsService(env); |
57 |
register_android_server_AlarmManagerService(env); |
58 |
register_android_server_BatteryService(env); |
59 |
register_android_server_UsbDeviceManager(env); |
60 |
register_android_server_UsbHostManager(env); |
61 |
register_android_server_VibratorService(env); |
62 |
register_android_server_SystemServer(env); |
63 |
register_android_server_location_GpsLocationProvider(env); |
64 |
register_android_server_connectivity_Vpn(env); |
65 |
register_android_server_HelloService(env); |
67 |
return JNI_VERSION_1_4; |
onload.cpp文件上部分为注册函数的声明,下部分为调用各种注册函数,而 这些注册函数就是JNI方法的注册函数! 正有通过这些注册函数,上层才有可能调用注册的JNI方法.
这些注册函数是由同目录下的其他.cpp文件中实现,如上面的 register_android_server_HelloService(env)这个函数是在 com_android_service_HelloService.cpp文件中实现的.那么编译器又是如何知道这点的呢? 答案当然是Android.mk这个文件,打开这个文件,其内容如下:
01 |
LOCAL_PATH:= $(call my- dir ) |
05 |
com_android_server_AlarmManagerService.cpp \ |
06 |
com_android_server_BatteryService.cpp \ |
07 |
com_android_server_InputApplicationHandle.cpp \ |
08 |
com_android_server_InputManager.cpp \ |
09 |
com_android_server_InputWindowHandle.cpp \ |
10 |
com_android_server_LightsService.cpp \ |
11 |
com_android_server_PowerManagerService.cpp \ |
12 |
com_android_server_SystemServer.cpp \ |
13 |
com_android_server_UsbDeviceManager.cpp \ |
14 |
com_android_server_UsbHostManager.cpp \ |
15 |
com_android_server_VibratorService.cpp \ |
16 |
com_android_server_location_GpsLocationProvider.cpp \ |
17 |
com_android_server_connectivity_Vpn.cpp \ |
18 |
com_android_server_HelloService.cpp \ |
23 |
frameworks/base/services \ |
24 |
frameworks/base/core/jni \ |
25 |
external/skia/include/core |
27 |
LOCAL_SHARED_LIBRARIES := \ |
41 |
ifeq ($(WITH_MALLOC_LEAK_CHECK), true ) |
42 |
LOCAL_CFLAGS += -DMALLOC_LEAK_CHECK |
45 |
LOCAL_MODULE:= libandroid_servers |
47 |
include $(BUILD_SHARED_LIBRARY) |
在LOCAL_SRC_FILE中给出了所有实现文件(cpp文件)的路径,因此编译就能找到各个注册函数对应的实现文件了.
接下来让我们来看看其中一个注册函数的具体实现过程是如何的,比如:register_android_server_HelloService(env),打开 com_android_service_HelloService.cpp文件,其下有注册函数的实现代码,如下:
1 |
int register_android_server_HelloService(JNIEnv *env) { |
2 |
return jniRegisterNativeMethods(env, "com/android/server/HelloService" , method_table, NELEM(method_table)); |
其中jniRegisterNativeMethods为注册JNI方法函数,此函数在JNI方法使用中非常重要,此函数的第二个参数为对应着java类即HelloService.java的文件名,第三个参数为注册的方法表:
2 |
static const JNINativeMethod method_table[] = { |
3 |
{ "init_native" , "()Z" , ( void *)hello_init}, |
4 |
{ "setVal_native" , "(I)V" , ( void *)hello_setVal}, |
5 |
{ "getVal_native" , "()I" , ( void *)hello_getVal}, |
接下来就是方法表内各个接口的实现代码了.
如hello_setVal函数的实现:
02 |
static void hello_setVal(JNIEnv* env, jobject clazz, jint value) { |
04 |
LOGI( "Hello JNI: set value %d to device." , val); |
06 |
LOGI( "Hello JNI: device is not open." ); |
10 |
hello_device->set_val(hello_device, val); |
方法列表中的hello_init的实现代码中展现了如何调用下层HAL提供的接口, 还记得上一章: Android中HAL如何向上层提供接口总结 一文中描述HAL是如何向上层提供接口的吗?这个hello_init函数的实现就是典型的调用HAL提供的初始化接口的例子,下面见hello_init这个函数的实现代码:
02 |
static inline int hello_device_open( const hw_module_t* module, struct hello_device_t** device) { |
03 |
return module->methods->open(module, HELLO_HARDWARE_MODULE_ID, ( struct hw_device_t**)device); |
06 |
static jboolean hello_init(JNIEnv* env, jclass clazz) { |
07 |
hello_module_t* module; |
09 |
LOGI( "Hello JNI: initializing......" ); |
10 |
if (hw_get_module(HELLO_HARDWARE_MODULE_ID, ( const struct hw_module_t**)&module) == 0) { |
11 |
LOGI( "Hello JNI: hello Stub found." ); |
12 |
if (hello_device_open(&(module->common), &hello_device) == 0) { |
13 |
LOGI( "Hello JNI: hello device is open." ); |
16 |
LOGE( "Hello JNI: failed to open hello device." ); |
19 |
LOGE( "Hello JNI: failed to get hello stub module." ); |
上述的module->methods->open这个open函数就是HAL提供的接口,其函数原型在hardware.h头文件中有定义,只能返回struct hw_device_t类型的指针,而在JNI方法中,我们关心的是struct hello_device_t,只有通过struct hello_device_t,我们才能获取其所有的成员函数(接HAL提供的接口函数),由于struct hello_device_t的第一个成员就是struct hw_device_t类型的数据,因此在这里可以将获取的struct hw_device_t强制转化为struct hello_device_t来用。还没有明白过来的,建议回过头去看上一篇文章: Android中HAL如何向上层提供接口总结 .
3 方法列表说明
关于static const JNINativeMethod method_table[]方法列表的原型如下:
Andoird 中使用了一种不同传统Java JNI的方式来定义其native的函数。其中很重要的区别是Andorid使用了一种Java 和 C 函数的映射表数组,并在其中描述了函数的参数和返回值。这个数组的类型就是JNINativeMethod,见上述定义.
第一个变量name是Java中函数的名字。第二个变量signature,用字符串是描述了函数的参数和返回值.第三个变量fnPtr是函数指针,指向C函数。
其中比较难以理解的是第二个参数,例如:
"()V"
"(II)V"
"(Ljava/lang/String;Ljava/lang/String;)V"
实际上这些字符是与函数的参数类型一一对应的。
"()" 中的字符表示参数,后面的则代表返回值。例如"()V" 就表示void Func();
"(II)V" 表示 void Func(int, int);
具体的每一个字符的对应关系如下
字符 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函数位于一个嵌入类,则用$作为类名间的分隔符。
例如 "(Ljava/lang/String;Landroid/os/FileUtils$FileStatus;)Z"
文章出处:http://blog.csdn.net/flydream0/article/details/7088514