文档章节

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

紫地瓜
 紫地瓜
发布于 2012/11/16 09:08
字数 1936
阅读 118
收藏 1
目前正在学习JNI,从一开始的一无所知,到现在的略知一二,走了不少弯路,为了让有兴趣的同行少走弯路,下面把我的经验记录下来,给大家一个参考:
1、先从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调用者合二为一。

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目录下



?
代码片段,双击复制
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

© 著作权归作者所有

共有 人打赏支持
紫地瓜
粉丝 27
博文 143
码字总数 212781
作品 0
日本
产品经理
转载 Cocos2d-x android项目移植——各类bug及常见问题解决方案全集

在学习IOS项目如何移植到Android的项目过程中,相信很多初学者会遇到各种各样的问题。特别是移植从事C++语言编程的。在大家学过 java,考试还考的不错。但是说到jni,就直接不知道了。在移植...

Silencer
2013/08/16
0
0
Android Studio 3.2 都有哪些更新?这些关键点不要错过

作者:Jamal Eason, Android 产品经理 点击这里即刻下载 Android Studio 3.2。 Android Studio 3.2 是应用开发者切入最新的 Android 9 Pie 发布版和构建新的 Android App Bundle 的最佳途径。...

Android_开发者
昨天
0
0
[Android]关于Native函数的debug

先占坑,有空发。 环境:WIN7 + eclipse + android sdk4.3 + ndk r9b 需求:安卓程序通过jni调用C代码,并对C代码进行debug。 纠结:1、使用真机,直接无法启动 2、使用模拟器,能够启动,但...

ouczxl
2014/04/24
0
0
android NDK 二、编译方法

1、NDK 一中已经提到,使用eclipse中的配置 ,自动编译 2、手动编译(推荐) 打开bash.exe(即启动cygwin) 使用cd $NDK 进入/cygdrive/e/android-ndk-r5/ 用cd进入到对应的目录; 进入对应的...

大凉龙雀
2015/05/16
0
1
Android 网络编程 目录

Android 网络编程 目录 Android 网络编程1 Http协议 to be continued... Android 架构师之路 目录 Android 架构师之路1 UML图之用例图 Android 架构师之路2 UML图之类图 Android 架构师之路3...

香沙小熊
06/21
0
0

没有更多内容

加载失败,请刷新页面

加载更多

【大福利】极客时间专栏返现二维码大汇总

我已经购买了如下专栏,大家通过我的二维码你可以获得一定额度的返现! 然后,再给大家来个福利,只要你通过我的二维码购买,并且关注了【飞鱼说编程】公众号,可以加我微信或者私聊我,我再...

飞鱼说编程
57分钟前
1
0
Spring5对比Spring3.2源码之容器的基本实现

最近看了《Spring源码深度解析》,该书是基于Spring3.2版本的,其中关于第二章容器的基本实现部分,目前spring5的实现方式已有较大改变。 Spring3.2的实现: public void testSimpleLoad(){...

Ilike_Java
今天
1
0
【王阳明心学语录】-001

1.“破山中贼易,破心中贼难。” 2.“夫万事万物之理不外于吾心。” 3.“心即理也。”“心外无理,心外无物,心外无事。” 4.“人心之得其正者即道心;道心之失其正者即人心。” 5.“无...

卯金刀GG
今天
2
0
OSChina 周三乱弹 —— 我们无法成为野兽

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @ _刚刚好: 霸王洗发水这波很骚 手机党少年们想听歌,请使劲儿戳(这里) hahahahahahh @嘻酱:居然忘了喝水。 让你喝可乐的话, 你准忘不了...

小小编辑
今天
9
0
vm GC 日志 配置及查看

-XX:+PrintGCDetails 打印 gc 日志 -XX:+PrintTenuringDistribution 监控晋升分布 -XX:+PrintGCTimeStamps 包含时间戳 -XX:+printGCDateStamps 包含时间 -Xloggc:<filename> 可以将数据保存为......

Canaan_
昨天
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部