目前正在学习JNI,从一开始的一无所知,到现在的略知一二,走了不少弯路,为了让有兴趣的同行少走弯路,下面把我的经验记录下来,给大家一个参考:
1、先从SimpleJNI说起:
在Android SDK的源码目录下./development/samples/SimpleJNI可以找到一个最简单的JNI例子,其文件树如下
.
|-- AndroidManifest.xml
|-- Android.mk
|-- jni
| |-- Android.mk
| `-- native.cpp
`-- src
`-- com
`-- example
`-- android
`-- simplejni
`-- SimpleJNI.java
该例子的主要思想是用JNI生成一个本地库libsimplejni.so,实现一个add(a,b)功能,然后通过SimpleJNI.java调用该库输出显示信息
此例子的Android.mk文件如下:
2 # bundles the shared library and calls it using JNI.
3
4 TOP_LOCAL_PATH: = $(call my - dir)
5
6 # Build activity
7
8 LOCAL_PATH: = $(TOP_LOCAL_PATH)
9 include $(CLEAR_VARS)
10
11 LOCAL_MODULE_TAGS : = samples
12
13 LOCAL_SRC_FILES : = $(call all - subdir - java - files) #查找当前目录下所有的java文件
14
15 LOCAL_PACKAGE_NAME : = SimpleJNI #编译一个java包:SimpleJNI.apk
16
17 LOCAL_JNI_SHARED_LIBRARIES : = libsimplejni #编译一个动态库:libsimplejni.so
18
19 LOCAL_PROGUARD_ENABLED : = disabled
20
21 include $(BUILD_PACKAGE)
22
23 # ============================================================
24
25 # Also build all of the sub - targets under this one: the shared library.
26 include $(call all - makefiles - under,$(LOCAL_PATH))
在Android SDK的根目录下面运行终端,输入如下编译命令:
将得到如下两个文件:
out/target/product/sdkDemo/system/app/SimpleJNI.apk
out/target/product/sdkDemo/system/lib/libsimplejni.so
JNI代码的目录为jni/vative.cpp,其内容如下:
2 #include < utils / Log.h >
3
4 #include < stdio.h >
5
6 #include " jni.h " // JNI相关的头文件
7
8 static jint add(JNIEnv * env, jobject thiz, jint a, jint b) { /* 定义Java方法add(),具有两个整数类型的参数和一个整数类型的返回值,由本地代码add函数实现 */
9 int result = a + b;
10 LOGI( " %d + %d = %d " , a, b, result);
11 return result;
12 }
13
14 static const char * classPathName = " com/example/android/simplejni/Native " ; // 类的路径名
15
16 static JNINativeMethod methods[ ] = { // 本地方法列表
17 { " add " , " (II)I " , ( void * )add },
18 };
19
20 /* 使用JNI的核心是JNINativeMethod结构体,这个结构体在jni.h中定义
21
22 typedef struct {
23
24 const char* name; /*JNI函数的名称 */
25
26 const char * signature; /* 描述JNI函数的参数和返回值 */
27
28 void * fnPtr; /* JNI函数对应的C(C++)语言函数指针 */
29
30 }JNINativeMethod;
31
32 关于参数和返回值的类型如下表:
33
34 Java 类型 JNI类型 对应字母
35 Java 布尔类型(boolean) jboolean(8位无符号) Z
36 Java字节( byte ) jbyte(8位有符号) B
37 Java字符( char ) jchar(16位无符号) C
38 Java短整型( short ) jshort(16位有符号) S
39 Java整型( int ) jint(32位有符号) I
40 Java长整型( long ) jlong(64位有符号) J
41 Java单精度浮点(folat) jfloat(IEEE754,32位) F
42 Java双精度浮点( double ) jdouble(IEEE754,64位) D
43 Java对象 jobject L
44 Java无返回值 void V
45
46
47 该例子里 " (II)I代表的是,有两个整型参数和一个整 "
48
49 */
50
51
52
53 /*
54 * Register several native methods for one class.
55
56 */
57 static int registerNativeMethods(JNIEnv * env, const char * className,
58 JNINativeMethod * gMethods, int numMethods)
59 {
60 jclass clazz;
61
62 clazz = env -> FindClass(className);
63 if (clazz == NULL) {
64 LOGE( " Native registration unable to find class '%s' " , className);
65 return JNI_FALSE;
66 }
67 if (env -> RegisterNatives(clazz, gMethods, numMethods) < 0 ) {
68 LOGE( " RegisterNatives failed for '%s' " , className);
69 return JNI_FALSE;
70 }
71
72 return JNI_TRUE;
73 }
74
75 /*
76 * Register native methods for all classes we know about.
77 *以下是注册JNI方法,它又调用registerNativeMethods()函数
78 * returns JNI_TRUE on success.
79 */
80 static int registerNatives(JNIEnv * env)
81 {
82 if ( ! registerNativeMethods(env, classPathName,
83 methods, sizeof (methods) / sizeof (methods[ 0 ]))) {
84 return JNI_FALSE;
85 }
86
87 return JNI_TRUE;
88 }
89
90
91 // ----------------------------------------------------------------------------
92
93 /*
94 * This is called by the VM when the shared library is first loaded.
95
96 *在加载库的过程中调用registerNatives()函数实现方法注册
97 */
98
99 typedef union {
100 JNIEnv * env;
101 void * venv;
102 } UnionJNIEnvToVoid;
103
104 jint JNI_OnLoad(JavaVM * vm, void * reserved)
105 {
106 UnionJNIEnvToVoid uenv;
107 uenv.venv = NULL;
108 jint result = - 1 ;
109 JNIEnv * env = NULL;
110
111 LOGI( " JNI_OnLoad " );
112
113 if (vm -> GetEnv( & uenv.venv, JNI_VERSION_1_4) != JNI_OK) {
114 LOGE( " ERROR: GetEnv failed " );
115 goto bail;
116 }
117 env = uenv.env;
118
119 if (registerNatives(env) != JNI_TRUE) {
120 LOGE( " ERROR: registerNatives failed " );
121 goto bail;
122 }
123
124 result = JNI_VERSION_1_4;
125
126 bail:
127 return result;
128 }
编译此JNI代码所需要的Android.mk如下:
2 #use by our example of how to bundleashared library with an APK.
3
4 LOCAL_PATH: = $(call my - dir)
5 include $(CLEAR_VARS)
6
7 LOCAL_MODULE_TAGS : = samples
8
9 # This is the target being built.
10 LOCAL_MODULE: = libsimplejni
11
12
13 # All of the source files that we will compile.
14 LOCAL_SRC_FILES: = \
15 native.cpp
16
17 # All of the shared libraries we link against.
18 LOCAL_SHARED_LIBRARIES : = \
19 libutils
20
21 # No static libraries.
22 LOCAL_STATIC_LIBRARIES : =
23
24 # Also need the JNI headers.
25 LOCAL_C_INCLUDES += \
26 $(JNI_H_INCLUDE)
27
28 # No special compiler flags.
29 LOCAL_CFLAGS +=
30
31 # Don ' t prelink this library. For more efficient code, you may want
32 # to add this library to the prelink map and set this to true . However,
33 # it ' s difficult to do this for applications that are not supplied as
34 # part of a system image.
35
36 LOCAL_PRELINK_MODULE : = false #不需要重新链接此库
37
38 include $(BUILD_SHARED_LIBRARY)
应用部分的代码目录为/src/com/example/android/simplejni/SimpleJNI.java,在这个类中Native类是对本地方法的封装,内容如下:
2 static {
3 // The runtime will add "lib" on the front and ".o" on the end of
4 // the name supplied to loadLibrary.
5 System.loadLibrary( " simplejni " ); // 加载本地库
6 }
7
8 static native int add( int a, int b); // 调用本地方法
9 }
在这个类中调用的过程如下:
2 /** Called when the activity is first created. */
3 @Override
4 public void onCreate(Bundle savedInstanceState) {
5 super .onCreate(savedInstanceState);
6 TextView tv = new TextView( this ); // 建立一个UI中的类TextView
7 int sum = Native.add( 2 , 3 ); // 通过封装类调用本地方法
8 tv.setText( " 2 + 3 = " + Integer.toString(sum)); // 设置显示内容
9 setContentView(tv);
10 }
11 }
通常JNI的使用自下而上有4个层次:本地库、JNI库、声明本地接口的Java类,Java调用者。在本例中,本地库和JNI库合二为一,声明本地接口的Java类和Java调用者合二为一。
2、将以上所得到的libsimplejni.so与SimpleJNI.apk两个文件从Ubuntu中拷贝出来,放置在windows C盘的根目录下,
运行Android模拟器
在windows的“运行”中输入cmd打开windows的命令窗口
输入cd c:\命令切换到C盘根目录下
然后输入adb version确实系统是否已经安装了adb工具,如果已经安装将得到如下内容
Android Debug Bridge version 1.0.26
如果没有安装,可以到\android-sdk-windows\tools目录下将adb.exe和AdbWinApi.dll两个文件拷贝到windows C盘的system32目录下即可
然后输入如下命令将libsamplejni.so拷贝到模拟器的system/lib目录下
再输入如下命令把SampleJNI.apk拷贝到模拟器的system/app目录下
上面可能遇到的问题解决办法:
(1)、提示failed to copy 'libsimplejni.so'to'/system/lib/libsimplejni.so':Read-only file system
这是因为当前状态下,此目录是一个只读目录,输入如下命令就可以获得写的权限
(2)、提示failed to copy 'libsimplejni.so'to'/system/lib/libsimplejni.so':Out of memory
这是因为建议模拟器的时候默认的系统memory太小了,关闭当前模拟器,输入如下命令就可以解决此问题
说明:其中Android2.2是我当前所建AVD的名称,128代表的是设置的系统memory的大小,输入此命令之后将会自动打开模拟器
一切正常后,输入相应命令后将得到:
40 KB / s (5188 bytes in 0.125s)
C:\ > adb push SimpleJNI.apk / system / app
52 KB / s (5064 bytes in 0.093s)
在模拟器中,我们将看到已经安装好了的Simple JNI运行它之后
将得到我们所期望的结果
2+3=5
写在最后,本人刚开始学习JNI相关的东西,有错误的地方还希望广大同行斧正!