Android JNI编程指南及模拟器配置问题

目前正在学习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文件如下:

  

复制代码
1  # This makefile shows how to build a shared library and an activity that
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的根目录下面运行终端,输入如下编译命令:

make SimpleJNI libsimplejni

将得到如下两个文件:

out/target/product/sdkDemo/system/app/SimpleJNI.apk
out/target/product/sdkDemo/system/lib/libsimplejni.so
JNI代码的目录为jni/vative.cpp,其内容如下:

 

 

View Code
复制代码
1  #define  LOG_TAG "simplejni native.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如下:

复制代码
1  # This makefile supplies the rules  for  building a library of JNI codefor
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类是对本地方法的封装,内容如下:

 
复制代码
1  class  Native {  // 定义Java的封装类
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  }
复制代码

 在这个类中调用的过程如下: 

复制代码
1  public class  SimpleJNI  extends  Activity {
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目录下

 

adb push libsamplejni.so  / system / lib

再输入如下命令把SampleJNI.apk拷贝到模拟器的system/app目录下

 

adb push SampleJNI.apk

上面可能遇到的问题解决办法:

(1)、提示failed to copy 'libsimplejni.so'to'/system/lib/libsimplejni.so':Read-only file system

这是因为当前状态下,此目录是一个只读目录,输入如下命令就可以获得写的权限

 

adb remount

(2)、提示failed to copy 'libsimplejni.so'to'/system/lib/libsimplejni.so':Out of memory

这是因为建议模拟器的时候默认的系统memory太小了,关闭当前模拟器,输入如下命令就可以解决此问题

 

emulator  - avd Android2. 2 - partition - size  128

说明:其中Android2.2是我当前所建AVD的名称,128代表的是设置的系统memory的大小,输入此命令之后将会自动打开模拟器

一切正常后,输入相应命令后将得到:

C:\ > adb push libsimplejni.so  / system / lib
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相关的东西,有错误的地方还希望广大同行斧正!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值