您的位置:首页 > 运维架构 > Linux

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

2011-06-03 16:18 387 查看
http://www.189works.com/article-18659-1.html

目前正在学习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相关的东西,有错误的地方还希望广大同行斧正!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: