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

目前正在学习JNI,从一开始的一无所知,到如今的略知一二,走了很多弯路,为了让有兴趣的同行少走弯路,下面把个人经验记录下来,给你们一个参考:
一、先从SimpleJNI提及:
在Android SDK的源码目录下./development/samples/SimpleJNI能够找到一个最简单的JNI例子,其文件树以下
?
代码片断,双击复制
01
02
03
04
05
06
07
08
09
10
11
12
.
|-- 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文件以下:

?
代码片断,双击复制
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
# This makefile shows how to build a shared library and an activity that
# bundles the shared library and calls it using JNI.
 
TOP_LOCAL_PATH:= $(call my-dir)
 
# Build activity
LOCAL_PATH:= $(TOP_LOCAL_PATH)
include $(CLEAR_VARS)
 
LOCAL_MODULE_TAGS := samples
LOCAL_SRC_FILES := $(call all-subdir-java-files) #查找当前目录下全部的java文件
LOCAL_PACKAGE_NAME := SimpleJNI                #编译一个java包:SimpleJNI.apk
LOCAL_JNI_SHARED_LIBRARIES := libsimplejni #编译一个动态库:libsimplejni.so
LOCAL_PROGUARD_ENABLED := disabled
 
include $(BUILD_PACKAGE)
 
# ============================================================
 
# Also build all of the sub-targets underthisone: the shared library.
include $(call all-makefiles-under,$(LOCAL_PATH))


在Android SDK的根目录下面运行终端,输入以下编译命令:
?
代码片断,双击复制
01
make SimpleJNI libsimplejni


将获得以下两个文件:
?
代码片断,双击复制
01
02
out/target/product/sdkDemo/system/app/SimpleJNI.apk
out/target/product/sdkDemo/system/lib/libsimplejni.so


JNI代码的目录为jni/vative.cpp,其内容以下:
?
代码片断,双击复制
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
View Code
#define LOG_TAG"simplejni native.cpp"
#include <utils/Log.h>
 
#include <stdio.h>
 
#include"jni.h"//JNI相关的头文件
   
  staticjint add(JNIEnv *env, jobject thiz, jint a, jint b) {   /*定义Java方法add(),具备两个整数类型的参数和一个整数类型的返回值,由本地代码add函数实现*/
int result = a + b;
     LOGI("%d + %d = %d", a, b, result);
return result;
}
 
staticconstchar*classPathName ="com/example/android/simplejni/Native";   //类的路径名
 
static JNINativeMethod methods[ ] = {     //本地方法列表
   {"add", "(II)I", (void*)add },
};
 
/*使用JNI的核心是JNINativeMethod结构体,这个结构体在jni.h中定义
 
typedef struct {
 
        const char* name;           /*JNI函数的名称*/
 
constchar* signature;     /*描述JNI函数的参数和返回值*/
 
void*           fnPtr;            /*JNI函数对应的C(C++)语言函数指针*/
 
}JNINativeMethod;
 
关于参数和返回值的类型以下表:
 
Java 类型 JNI类型 对应字母
Java 布尔类型(boolean) jboolean(8位无符号) Z
Java字节(byte) jbyte(8位有符号) B
Java字符(char) jchar(16位无符号) C
Java短整型(short) jshort(16位有符号) S
Java整型(int) jint(32位有符号) I
Java长整型(long) jlong(64位有符号) J
Java单精度浮点(folat) jfloat(IEEE754,32位) F
Java双精度浮点(double) jdouble(IEEE754,64位) D
Java对象 jobject L
Java无返回值 void V
 
 
该例子里"(II)I表明的是,有两个整型参数和一个整"
 
*/
/*
  * Register several native methods for one class.
 
*/
staticint registerNativeMethods(JNIEnv* env, constchar* className,
     JNINativeMethod* gMethods, int numMethods)
{
     jclass clazz;
 
     clazz = env->FindClass(className);
if (clazz == NULL) {
         LOGE("Native registration unable to find class '%s'", className);
return JNI_FALSE;
     }
if (env->RegisterNatives(clazz, gMethods, numMethods) <0) {
         LOGE("RegisterNatives failed for '%s'", className);
return JNI_FALSE;
     }
 
return JNI_TRUE;
}
 
/*
  * Register native methods for all classes we know about.
  *如下是注册JNI方法,它又调用registerNativeMethods()函数
  * returns JNI_TRUE on success.
*/
staticint registerNatives(JNIEnv* env)
{
if (!registerNativeMethods(env, classPathName,
                  methods, sizeof(methods) /sizeof(methods[0]))) {
return JNI_FALSE;
   }
 
return JNI_TRUE;
}
 
// ----------------------------------------------------------------------------
 
/*
  * This is called by the VM when the shared library is first loaded.
 
*在加载库的过程当中调用registerNatives()函数实现方法注册
*/
   
typedef union {
     JNIEnv* env;
void* venv;
} UnionJNIEnvToVoid;
 
jint JNI_OnLoad(JavaVM* vm,void* reserved)
{
     UnionJNIEnvToVoid uenv;
     uenv.venv = NULL;
     jint result =-1;
     JNIEnv* env = NULL;
    
     LOGI("JNI_OnLoad");
 
if(vm->GetEnv(&uenv.venv, JNI_VERSION_1_4) != JNI_OK) {
         LOGE("ERROR: GetEnv failed");
gotobail;
     }
     env = uenv.env;
 
if(registerNatives(env) != JNI_TRUE) {
         LOGE("ERROR: registerNatives failed");
gotobail;
     }
    
     result = JNI_VERSION_1_4;
    
bail:
returnresult;
}


编译此JNI代码所须要的Android.mk以下:
?
代码片断,双击复制
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# This makefile supplies the rulesforbuilding a library of JNI codefor
#use by our example of how to bundleashared library with an APK.
 
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
 
LOCAL_MODULE_TAGS := samples
 
# This is the target being built.
LOCAL_MODULE:= libsimplejni
 
# All of the source files that we will compile.
LOCAL_SRC_FILES:= \
   native.cpp
 
# All of the shared libraries we link against.
LOCAL_SHARED_LIBRARIES := \
  libutils
 
# Nostaticlibraries.
LOCAL_STATIC_LIBRARIES :=
 
# Also need the JNI headers.
LOCAL_C_INCLUDES += \
  $(JNI_H_INCLUDE)
 
# No special compiler flags.
LOCAL_CFLAGS +=
 
# Don't prelinkthislibrary.  For more efficient code, you may want
# to addthislibrary to the prelink map and setthis totrue. However,
# it's difficult todothisforapplications that are not supplied as
# part of a system image.
 
LOCAL_PRELINK_MODULE :=false     #不须要从新连接此库
 
include $(BUILD_SHARED_LIBRARY)


应用部分的代码目录为/src/com/example/android/simplejni/SimpleJNI.java,在这个类中Native类是对本地方法的封装,内容以下:

?
代码片断,双击复制
01
02
03
04
05
06
07
08
09
10
classNative {         
  //定义Java的封装类
static{
// The runtime will add "lib" on the front and ".o" on the end of
// the name supplied to loadLibrary.
         System.loadLibrary("simplejni");           //加载本地库
     }
 
staticnativeint add(inta,intb);              //调用本地方法
}


在这个类中调用的过程以下: 
?
代码片断,双击复制
01
02
03
04
05
06
07
08
09
10
11
publicclass SimpleJNIextendsActivity {
/** Called when the activity is first created. */
     @Override
publicvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
         TextView tv =newTextView(this);                                    //创建一个UI中的类TextView
intsum = Native.add(2,3);                                                //经过封装类调用本地方法
         tv.setText("2 + 3 = "+ Integer.toString(sum));                  //设置显示内容
         setContentView(tv);
     }
}


一般JNI的使用自下而上有4个层次:本地库、JNI库、声明本地接口的Java类,Java调用者。在本例中,本地库和JNI库合二为一,声明本地接口的Java类和Java调用者合二为一。

二、将以上所获得的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目录下



?
代码片断,双击复制
01
adb push libsamplejni.so /system/lib


再输入以下命令把SampleJNI.apk拷贝到模拟器的system/app目录下
?
代码片断,双击复制
01
adb push SampleJNI.apk


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

(1)、提示failed to copy 'libsimplejni.so'to'/system/lib/libsimplejni.so':Read-only file system
这是由于当前状态下,此目录是一个只读目录,输入以下命令就能够得到写的权限

?
代码片断,双击复制
01
adb remount


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

这是由于建议模拟器的时候默认的系统memory过小了,关闭当前模拟器,输入以下命令就能够解决此问题
?
代码片断,双击复制
01
emulator -avd Android2.2-partition-size128


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

一切正常后,输入相应命令后将获得:
?
代码片断,双击复制
01
02
03
04
05
C:\>adb push libsimplejni.so /system/lib
40KB/s (5188bytes in0.125s)
 
C:\>adb push SimpleJNI.apk /system/app
52KB/s (5064bytes in0.093s)


在模拟器中,咱们将看到已经安装好了的Simple JNI运行它以后 将获得咱们所指望的结果 2+3=5
相关文章
相关标签/搜索