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

来源:互联网 发布:第3套人民币10元爱淘宝 编辑:程序博客网 时间:2024/06/08 05:24

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相关的东西,有错误的地方还希望广大同行斧正!

原创粉丝点击