Android系统JNI的实现方式
All rights reserved
JNI(Java Native Interface)定义了一种Java代码调用C或者C++代码等其他代码的方式。
在Android系统中,JNI通过JNINativeMethod结构体进行描述,该结构体定义于jni.h,如下所示:
1
2
3
4
5
|
typedef struct {
const
char
* name;
const
char
* signature;
void
* fnPtr;
} JNINativeMethod;
|
第一个参数name:是Java代码中的函数名。
第二个参数signature:用于描述函数的参数和返回值。
第三个参数fnPtr:C代码中函数的指针。
其中,第二个参数为一个描述函数参数和返回值的字符串,字符串的格式如下:
(XX..)X
X的取值和定义如下所示:
字符 | Java类型 | C类型 |
V | void | void |
Z | jboolean | unsigned char |
B | jbyte | signed char |
C | jchar | unsigned short |
S | jshort | short |
I | jint | int |
J | jlong | long |
F | jfloat | float |
D | jdouble | double |
另外,从jni.h中对于变量类型的定义中也可以看到这些字符的意义,如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
typedef unsigned
char
jboolean;
/* unsigned 8 bits */
typedef signed
char
jbyte;
/* signed 8 bits */
typedef unsigned
short
jchar;
/* unsigned 16 bits */
typedef
short
jshort;
/* signed 16 bits */
typedef
int
jint;
/* signed 32 bits */
typedef
long
long
jlong;
/* signed 64 bits */
typedef
float
jfloat;
/* 32-bit IEEE 754 */
typedef
double
jdouble;
/* 64-bit IEEE 754 */
typedef union jvalue {
jboolean z;
jbyte b;
jchar c;
jshort s;
jint i;
jlong j;
jfloat f;
jdouble d;
jobject l;
} jvalue;
|
例如,为一个驱动添加HAL层,并创建JNI层。仅就JNI层而言,创建frameworks/base/services/jni/com_android_server_DemoService.cpp文件,该文件中用于描述JNI接口的代码如下所示:
1
2
3
4
5
|
static
const
JNINativeMethod method_table[] = {
{
"init_native"
,
"()I"
, (
void
*)demo_init},
{
"setVal_native"
,
"(II)V"
, (
void
*)demo_setVal},
{
"getVal_native"
,
"(I)I"
, (
void
*)demo_getVal},
};
|
其中“(II)V”,表示函数的有两个整形参数,返回值为void。
注意:由参数二指定的函数参数和返回值类型一定要和C函数的参数和返回值保持一致,否则虽然编译能够通过,但在Android系统加载过程中,会报如下所示的错误,导致Android系统无法正常运行。
1
2
3
4
5
|
E/dalvikvm(
1737
): ERROR: couldn't find
native
method
E/dalvikvm(
1737
): Requested: Lcom/android/server/DemoService;.init_native:()Z
E/dalvikvm(
1737
): Candidate: Lcom/android/server/DemoService;.init_native:()I
E/JNIHelp (
1737
): RegisterNatives failed
for
'com/android/server/DemoService'
, aborting
F/libc (
1737
): Fatal signal
11
(SIGSEGV) at
0xdeadbaad
(code=
1
), thread
1737
(system_server)
|
以上错误通发生在frameworks/base/services/jni/onload.cpp文件中的JNI_OnLoad()函数中,如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
extern
"C"
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(
"GetEnv failed!"
);
return
result;
}
ALOG_ASSERT(env,
"Could not retrieve the env!"
);
……
<strong>register_android_server_DemoService(env);</strong>
……
return
JNI_VERSION_1_4;
}
|
另外,ProGuard对程序的优化也可能导致上述运行错误的发生。此时,可以在makefile文件中添加“LOCAL_PROGUARD_ENABLED:=disabled”宏来关闭ProGuard的优化。
关于ProGuard,可以参考其官方网站:http://proguard.sourceforge.net/
ProGuard是一个免费的Java类文件压缩器、优化器、混淆器和预校验器。它会检测并删除没有用到的类、域、方法以及属性。它最大限度的优化字节码并且删除无用的指令。它用很短的没有意义的名字对剩余的类、域和方法进行重命名。最后,它对处理过的代码进行预校验。
ProGuard的一些用途如下:
A.为了更小的代码档案、更快的网络传输、更快的加载速度和更小的内存占用创建更紧凑的代码;
B.使程序和库难于进行反向工程;
C.列出死代码,这样就能将其删除;
D.为Java 6或更高的版本对存在的类文件进行重定位和预校验,以充分利用其快速加载类的性能。