NDK项目源码地址 :
— 第一个JNI示例程序下载 : GitHub – https://github.com/han1202012/NDKHelloworld.git
— Java传递参数给C语言实例程序 : GitHub – https://github.com/han1202012/NDKParameterPassing.git
—C语言回调Java方法示例程序 : GitHub – https://github.com/han1202012/NDK_Callback.git
—分析Log框架层JNI源码所需的Android底层文件 : CSDN – http://download.csdn.net/detail/han1202012/6905507
开发环境介绍 :
— eclipse : adt-bundle-windows-x86-20130917
— sdk : 版本 2.3.3
— ndk : android-ndk-r9c-windows-x86.zip
— cygwin : 所需组件 binutils , gcc , gcc-mingw , gdb , make;
— javah : jdk6.0自带工具
— javap : jdk6.0自带工具
**JNI 总结 : **
Java 调用 C 流程 :
— a. 定义 Native 方法 : 在 shuliang.han.ndkparameterpassing.DataProvider.java 类中定义 Native 方法 public native int add(int x, int y);
— b. 生成方法签名 : 进入 AndroidProject/bin/classes 目录, 使用 **javah **shuliang.han.ndkparameterpassing.DataProvider 命令, 便生成了头文件, 该头文件引用了 jni.h, 以及定义好了 对应的 Native 方法, 生成 JNIEXPORT jint JNICALL Java_shuliang_han_ndkparameterpassing_DataProvider_add (JNIEnv *, jobject, jint, jint);
— c. 编写 Android.mk 文件 :
— d. 生成 动态库 so 文件 : 进入 Android.mk 所在目录, 在该目录执行 ndk 下的 ndk-build 命令;
— e. Java代码加载动态库 : 在 Java 代码中调用该类的类前面, 在类的一开始, 不在方法中, 加入 static{ System.loadLibrary(“hello”); } ;
一. JNI介绍
1. JNI引入
JNI概念 : Java本地接口,Java Native Interface, 它是一个协议, 该协议用来沟通Java代码和外部的本地C/C++代码, 通过该协议 Java代码可以调用外部的本地代码, 外部的C/C++ 代码可以调用Java代码;
C和Java的侧重 :
— C语言 : C语言中最重要的是 函数 function;
— Java语言 : Java中最重要的是 JVM, class类, 以及class中的方法;
C与Java如何交流 :
— JNI规范 : C语言与Java语言交流需要一个适配器, 中间件, 即 JNI, JNI提供了一种规范;
— C语言中调用Java方法 : 可以让我们在C代码中找到Java代码class中的方法, 并且调用该方法;
— Java语言中调用C语言方法 : 同时也可以在Java代码中, 将一个C语言的方法映射到Java的某个方法上;
— JNI桥梁作用 : JNI提供了一个桥梁, 打通了C语言和Java语言之间的障碍;
JNI中的一些概念 :
— native : Java语言中修饰本地方法的修饰符, 被该修饰符修饰的方法没有方法体;
— Native方法 : 在Java语言中被native关键字修饰的方法是Native方法;
— JNI层 : Java声明Native方法的部分;
— JNI函数 : JNIEnv提供的函数, 这些函数在jni.h中进行定义;
— JNI方法 : Native方法对应的JNI层实现的 C/C++方法, 即在jni目录中实现的那些C语言代码;
2. Android中的应用程序框架
正常情况下的Android框架 : 最顶层是Android的应用程序代码, 上层的应用层 和 应用框架层 主要是Java代码, 中间有一层的Framework框架层代码是 C/C++代码, 通过Framework进行系统调用, 调用底层的库 和linux 内核;
使用JNI时的Android框架 : 绕过Framework提供的调用底层的代码, 直接调用自己写的C代码, 该代码最终会编译成为一个库, 这个库通过JNI提供的一个Stable的ABI 调用linux kernel;ABI是二进制程序接口 application binary interface.
纽带 : JNI是连接框架层 (Framework – C/C++) 和**应用框架层(Application Framework – Java)**的纽带;
JNI在Android中作用 : JNI可以调用本地代码库(即C/C++代码), 并通过 Dalvik虚拟机 与应用层 和 应用框架层进行交互, Android中JNI代码主要位于应用层 和 应用框架层;
— 应用层 : 该层是由JNI开发, 主要使用标准JNI编程模型;
— 应用框架层 : 使用的是Android中自定义的一套JNI编程模型, 该自定义的JNI编程模型弥补了标准JNI编程模型的不足;
Android中JNI源码位置 : 在应用框架层中, 主要的JNI代码位于 framework/base目录下, 这些模块被编译成共享库之后放在 /system/lib 目录下;
NDK与JNI区别 :
— NDK: NDK是Google开发的一套开发和编译工具集, 主要用于Android的JNI开发;
— JNI : JNI是一套编程接口, 用来实现Java代码与本地的C/C++代码进行交互;
JNI编程步骤:
— 声明native方法 : 在Java代码中声明 native method()方法;
— 实现JNI的C/C++方法 : 在JNI层实现Java中声明的native方法, 这里使用javah工具生成带方法签名的头文件, 该JNI层的C/C++代码将被编译成动态库;
— 加载动态库 : 在Java代码中的静态代码块中加载JNI编译后的动态共享库;
.
3. JNI作用
JNI作用 :
— 扩展: JNI扩展了JVM能力, 驱动开发, 例如开发一个wifi驱动, 可以将手机设置为无限路由;
— 高效 : 本地代码效率高, 游戏渲染, 音频视频处理等方面使用JNI调用本地代码, C语言可以灵活操作内存;
— 复用 : 在文件压缩算法 7zip开源代码库, 机器视觉 openCV开放算法库 等方面可以复用C平台上的代码, 不必在开发一套完整的Java体系, 避免重复发明轮子;
— 特殊 : 产品的核心技术一般也采用JNI开发, 不易破解;
Java语言执行流程 :
— 编译字节码 : Java编译器编译 .java源文件, 获得.class 字节码文件;
— 装载类库 : 使用类装载器装载平台上的Java类库, 并进行字节码验证;
— Java虚拟机 : 将字节码加入到JVM中, Java解释器 和 即时编译器 同时处理字节码文件, 将处理后的结果放入运行时系统;
— 调用JVM所在平台类库 : JVM处理字节码后, 转换成相应平台的操作, 调用本平台底层类库进行相关处理;
Java一次编译到处执行 : JVM在不同的操作系统都有实现, Java可以一次编译到处运行, 字节码文件一旦编译好了, 可以放在任何平台的虚拟机上运行;
.
二. NDK详解
1. 交叉编译库文件
C代码执行 : C代码被编译成库文件之后, 才能执行, 库文件分为动态库 和静态库 两种;
— 动态库 : unix环境下**.so 后缀的是动态库, windows环境下.dll 后缀**的是动态库; 动态库可以依赖静态库加载一些可执行的C代码;
— 静态库 :.a 后缀是静态库的扩展名;
库文件来源 : C代码 进行 编译 链接操作之后, 才会生成库文件, 不同类型的CPU 操作系统 生成的库文件是不一样;
— CPU分类 : arm结构, 嵌入式设备处理器; x86结构, pc 服务器处理器; 不同的CPU指令集不同;
— 交叉编译 :windows x86编译出来的库文件可以在arm平台运行的代码;
— 交叉编译工具链 : Google提供的 NDK 就是交叉编译工具链, 可以在linux环境下编译出在arn平台下执行的二进制库文件;
NDK作用 : 是Google提供了交叉编译工具链, 能够在linux平台编译出在arm平台下执行的二进制库文件;
NDK版本介绍 : android-ndk-windows 是在windows系统中的cygwin使用的, android-ndk-linux 是在linux下使用的;
2. 部署NDK开发环境
(1) 下载Cygwin安装器
下载地址 : http://cygwin.com/setup-x86.exe , 这是下载器, 可以使用该下载器在线安装, 也可以将cygwin下载到本地之后, 在进行安装;
安装器使用 : Cygwin的下载, 在线安装, 卸载 等操作都有由该安装器进行;
— 本地文件安装 : 选择安装文件所在的目录, 然后选择所要安装的安装包;
— 在线安装 : 选择在线安装即可, 然后选择需要的安装包;
— 卸载 : windows上使用其它软件例如360, 控制面板中是无法卸载Cygwin的, 只能通过安装器来卸载;
(2) 安装Cygin
双击安装器 setup-x86.exe 下一步 :
选择安装方式 :
— 在线安装 : 直接下载, 然后安装;
— 下载安装文件 : 将安装文件下载下来, 可以随时安装, 注意安装文件也需要安装器来进行安装;
— 从本地文件安装 : 即使用下载的安装文件进行安装;
选择Cygwin安装位置 :
选择下载好安装文件位置 : 之前我下了一个完全版的Cygwin, 包括了所有的Cygwin组件, 全部加起来有5.23G, 下载速度很快, 使用网易的镜像, 基本可以全速下载;
选择需要安装Cygwin组件 : 这里我们只需要以下组件 : binutils , gcc , gcc-mingw , gdb , make , 不用下全部的组件;
之后点击下一步等待完成安装即可;
.
安装完之后, 打开bash命令窗口, 可以设置下显示的字体, 使用 make -version 查看是否安装成功 :
(3) Cygwin目录介绍
以下是Cygwin安装目录的情况 : 该安装目录就是所模拟的linux 的根目录;
对应的linux目录 : 这两个目录进行对比发现, 两个目录是一样的, Cygwin的安装目录就是 linux根目录;
cygdrive目录 : 该目录是Cygwin模拟出来的windows目录结构, 进入该目录后, 会发现windows的盘符目录, 通过该目录可以访问windows中的文件;
(4) 下载NDK工具
从Google的Android开发者官网上下载该工具, 注意NDK工具分类 : 下载地址 –http://developer.android.com/tools/sdk/ndk/index.html -;
— windows版本NDK:android-ndk-r9c-windows-x86.zip (32位),android-ndk-r9c-windows-x86_64.zip (64位) 该版本是用在windows上的Cygwin下, 不能直接在windows上直接运行;
— linux版本NDK :android-ndk-r9c-linux-x86.tar.bz2(32位) , android-ndk-r9c-linux-x86_64.tar.bz2 (64位) , 该版本直接在linux下执行即可;
在这里下载windows版本的NDK, 运行在Cygwin上;
(4) NDK环境介绍
NDK工具的文件结构 :
ndk-build脚本 : NDK build 脚本是 gun-make 的简单封装, gun-make 是编译C语言代码的工具, 该脚本执行的前提是linux环境下必须安装 make 程序;
**NDK安装在Cygwin中** : 将NDK压缩文件拷贝到Cygwin的根目录中, 解压 : android-ndk-r9c 目录就是NDK目录;
**执行以下NDK目录下的 ndk-build 命令** : ./ndk-build ;
**执行结果** :
<div>

</div>
<div>
— **提交缓存** : git commit -m ‘提交’;
</div>
<div>

— 提交到远程GitHub仓库 : git push -u origin master ;
<div>

</div>
<div>
</div>
<div>
**GitHub项目** :
</div>
<div>

</div>
<div>
</div>
<div>
</div>
<div>
</div>
<div>
</div>
## <a name="t29"></a>3. 项目讲解
<div>
</div>
### <a name="t30"></a>(1) Android.mk文件讲解
<div>
</div>
<div>
**Android.mk文件内容** :
</div>
<div>
</div>
<div>
<div class="dp-highlighter bg_plain">
<div class="bar">
<div class="tools">
**[plain]** [view plain](http://blog.csdn.net/shulianghan/article/details/18964835#)[copy](http://blog.csdn.net/shulianghan/article/details/18964835#)[](https://code.csdn.net/snippets/181707)[](https://code.csdn.net/snippets/181707/fork)
<div>
<embed id="ZeroClipboardMovie_13" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_13">
</embed>
</div>
</div>
</div>
- LOCAL_PATH := $(call my-dir)
-
- include $(CLEAR_VARS)
-
- LOCAL_MODULE := hello
- LOCAL_SRC_FILES := hello.c
-
- include $(BUILD_SHARED_LIBRARY)
</div>
<div>
</div>
<div>
**获取当前文件内容** : $(call my-dir) 是编译器中的宏方法, 调用该宏方法, 就会**返回前的目录路径**;
</div>
<div>
**赋值符号** : ” := ” 是**赋值符号**, 第一句话 是 返回当前文件所在的当前目录, 并将这个目录路径赋值给 LOCAL_PATH;
</div>
<div>
**初始化编译模块参数** : $(CLEAR_VARS) 作用是将编译模块的参数初始化, LOCAL_MODULE LOCAL_SRC_FILES 也是这样的参数;
</div>
**指定编译模块** : LOCAL_MODULE := hello , 指定编译后的 so 文件名称, 编译好之后系统会在该名称前面加上 “lib”, 后缀加上 “.so”;</div>
<div>
**指定编译源文件** : LOCAL_SRC_FILES := hello.c 告诉编译系统源文件, 如果有多个文件那么就依次写在后面即可;
</div>
<div>
**编译成静态库** : include $(BUILD_SHARED_LIBRARY), 作用是高速系统, 编译的结果编译成 .so 后缀的静态库;
</div>
<div>
</div>
<div>
**静态库引入** : NDK的platform中有很多 “.a” 结尾的动态库, 我们编译动态库的时候, 可以将一些静态库引入进来;
</div>
<div>
</div>
<div>
</div>
### <a name="t31"></a>(2) 自动生成方法签名
<div>
</div>
<div>
</div>
<div>
**使用javah工具** : 在C中实现Java调用的jni方法, 方法的签名很复杂, 需要将完整的包名类名方法名都要使用 “_” 连接起来, 很麻烦, jdk提供的生成签名方法的工具;
</div>
<div>
</div>
<div>
**遗留问题** : 目前查到的方法是 在bin目录下 执行 javah -jni 包名类名 命令, 但是执行不成功, 暂时没找到解决方案;
</div>
<div>
—** Android中会自动生成** .class文件吗, 没发现啊, PY人!
</div>
<div>

</div>
<div>
</div>
<div>
**解决问题** : 在jni目录下存在classes目录, 但是这个目录在eclipse中不显示, 这里我们要注意;
</div>
<div>

</div>
<div>
</div>
<div>
**在Cygwin中使用 javah 命令即可** :
</div>
<div>

</div>
<div>
**生成的头文件** : shuliang_han_ndkparameterpassing_DataProvider.h;
</div>
<div>
<div class="dp-highlighter bg_cpp">
<div class="bar">
<div class="tools">
**[cpp]** [view plain](http://blog.csdn.net/shulianghan/article/details/18964835#)[copy](http://blog.csdn.net/shulianghan/article/details/18964835#)[](https://code.csdn.net/snippets/181707)[](https://code.csdn.net/snippets/181707/fork)
<div>
<embed id="ZeroClipboardMovie_14" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_14">
</embed>
</div>
</div>
</div>
- <span class="comment">/* DO NOT EDIT THIS FILE – it is machine generated */</span>
- <span class="preprocessor">#include <jni.h></span>
- <span class="comment">/* Header for class shuliang_han_ndkparameterpassing_DataProvider */</span>
-
- <span class="preprocessor">#ifndef _Included_shuliang_han_ndkparameterpassing_DataProvider</span>
- <span class="preprocessor">#define _Included_shuliang_han_ndkparameterpassing_DataProvider</span>
- <span class="preprocessor">#ifdef __cplusplus</span>
- <span class="keyword">extern</span> <span class="string">“C”</span> {
- <span class="preprocessor">#endif</span>
- <span class="comment">/*</span>
- <span class="comment"> * Class: shuliang_han_ndkparameterpassing_DataProvider</span>
- <span class="comment"> * Method: add</span>
- <span class="comment"> * Signature: (II)I</span>
- <span class="comment"> */</span>
- JNIEXPORT jint JNICALL Java_shuliang_han_ndkparameterpassing_DataProvider_add
- (JNIEnv *, jobject, jint, jint);
-
- <span class="comment">/*</span>
- <span class="comment"> * Class: shuliang_han_ndkparameterpassing_DataProvider</span>
- <span class="comment"> * Method: sayHelloInc</span>
- <span class="comment"> * Signature: (Ljava/lang/String;)Ljava/lang/String;</span>
- <span class="comment"> */</span>
- JNIEXPORT jstring JNICALL Java_shuliang_han_ndkparameterpassing_DataProvider_sayHelloInc
- (JNIEnv *, jobject, jstring);
-
- <span class="comment">/*</span>
- <span class="comment"> * Class: shuliang_han_ndkparameterpassing_DataProvider</span>
- <span class="comment"> * Method: intMethod</span>
- <span class="comment"> * Signature: ([I)[I</span>
- <span class="comment"> */</span>
- JNIEXPORT jintArray JNICALL Java_shuliang_han_ndkparameterpassing_DataProvider_intMethod
- (JNIEnv *, jobject, jintArray);
-
- <span class="preprocessor">#ifdef __cplusplus</span>
- }
- <span class="preprocessor">#endif</span>
- <span class="preprocessor">#endif</span>
</div>
</div>
<div>
</div>
<div>
.
</div>
<div>
</div>
<div>
</div>
### <a name="t32"></a>(3) NDK开发中乱码问题
<div>
</div>
<div>
**解决乱码思路** : C语言编译的时候用的是 ISO-8859-1 码表进行编码, 如果我们使用C语言jni开发, 需要进行转码操作;
</div>
<div>
— 将ISO-8859-1转为UTF-8字符: String string = new String(str.getBytes(“iso8859-1”), “UTF-8”);
</div>
<div>
</div>
<div>
</div>
<div>
示例 :
</div>
<div>
</div>
<div>
**添加中文jni调用** : 将jni中的hello.c 中返回的字符串修改为中文, 重新编译 .so 静态库文件;
</div>
<div>
— **修改后的hello.c文件如下** : 只改变了返回的字符串, 添加了中文;
</div>
<div>
<div class="dp-highlighter bg_cpp">
<div class="bar">
<div class="tools">
**[cpp]** [view plain](http://blog.csdn.net/shulianghan/article/details/18964835#)[copy](http://blog.csdn.net/shulianghan/article/details/18964835#)[](https://code.csdn.net/snippets/181707)[](https://code.csdn.net/snippets/181707/fork)
<div>
<embed id="ZeroClipboardMovie_15" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_15">
</embed>
</div>
</div>
</div>
- <span class="preprocessor">#include <jni.h></span>
-
- <span class="comment">/*</span>
- <span class="comment"> * 方法名称规定 : Java_完整包名类名_方法名()</span>
- <span class="comment"> * JNIEnv 指针</span>
- <span class="comment"> *</span>
- <span class="comment"> * 参数介绍 :</span>
- <span class="comment"> * env : 代表Java环境, 通过这个环境可以调用Java中的方法</span>
- <span class="comment"> * thiz : 代表调用JNI方法的对象, 即MainActivity对象</span>
- <span class="comment"> */</span>
- jstring Java_shuliang_han_ndkhelloworld_MainActivity_helloFromJNI(JNIEnv *env, jobject thiz)
- {
- <span class="comment">/*</span>
- <span class="comment"> * 调用 android-ndk-r9c\platforms\android-8\arch-arm\usr\include 中jni.h中的方法</span>
- <span class="comment"> * jni.h 中定义的方法 jstring (*NewStringUTF)(JNIEnv*, const char*);</span>
- <span class="comment"> */</span>
- <span class="keyword">return</span> (*env)->NewStringUTF(env, <span class="string">“hello world jni 中文”</span>);
- }
</div>
**使用NDK重新编译hello.c文件** : 修改了C源码之后, 重新将该c文件编译成so文件;</div>
<div>
— **编译过程**: 打开cygwin, 进入cygdrive/ 下对应windows中源码项目中的jni目录, 执行 /android-ndk-r9c/ndk-build 命令;
</div>
<div>

</div>
<div>
</div>
<div>
</div>
<div>
**运行Android代码报错** : 因为jni中c文件有中文, 中文不能被识别;
</div>
<div>
<div class="dp-highlighter bg_java">
<div class="bar">
<div class="tools">
**[java]** [view plain](http://blog.csdn.net/shulianghan/article/details/18964835#)[copy](http://blog.csdn.net/shulianghan/article/details/18964835#)[](https://code.csdn.net/snippets/181707)[](https://code.csdn.net/snippets/181707/fork)
<div>
<embed id="ZeroClipboardMovie_16" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_16">
</embed>
</div>
</div>
</div>
- <span class="number">01</span>–<span class="number">31</span> <span class="number">14</span>:<span class="number">36</span>:<span class="number">04.803</span>: W/dalvikvm(<span class="number">389</span>): JNI WARNING: illegal continuation <span class="keyword">byte</span> <span class="number">0xd0</span>
- <span class="number">01</span>–<span class="number">31</span> <span class="number">14</span>:<span class="number">36</span>:<span class="number">04.803</span>: W/dalvikvm(<span class="number">389</span>): string: <span class="string">‘hello world jni ????’</span>
- <span class="number">01</span>–<span class="number">31</span> <span class="number">14</span>:<span class="number">36</span>:<span class="number">04.803</span>: W/dalvikvm(<span class="number">389</span>): in Lshuliang/han/ndkhelloworld/MainActivity;.helloFromJNI ()Ljava/lang/String; (NewStringUTF)
- <span class="number">01</span>–<span class="number">31</span> <span class="number">14</span>:<span class="number">36</span>:<span class="number">04.834</span>: I/dalvikvm(<span class="number">389</span>): <span class="string">“main”</span> prio=<span class="number">5</span> tid=<span class="number">1</span> NATIVE
- <span class="number">01</span>–<span class="number">31</span> <span class="number">14</span>:<span class="number">36</span>:<span class="number">04.834</span>: I/dalvikvm(<span class="number">389</span>): | group=<span class="string">“main”</span> sCount=<span class="number"></span> dsCount=<span class="number"></span> obj=<span class="number">0x4001f1a8</span> self=<span class="number">0xce48</span>
- <span class="number">01</span>–<span class="number">31</span> <span class="number">14</span>:<span class="number">36</span>:<span class="number">04.834</span>: I/dalvikvm(<span class="number">389</span>): | sysTid=<span class="number">389</span> nice=<span class="number"></span> sched=<span class="number"></span>/<span class="number"></span> cgrp=<span class="keyword">default</span> handle=-<span class="number">1345006528</span>
- <span class="number">01</span>–<span class="number">31</span> <span class="number">14</span>:<span class="number">36</span>:<span class="number">04.844</span>: I/dalvikvm(<span class="number">389</span>): | schedstat=( <span class="number">257006717</span> <span class="number">305462830</span> <span class="number">51</span> )
- <span class="number">01</span>–<span class="number">31</span> <span class="number">14</span>:<span class="number">36</span>:<span class="number">04.844</span>: I/dalvikvm(<span class="number">389</span>): at shuliang.han.ndkhelloworld.MainActivity.helloFromJNI(Native Method)
- <span class="number">01</span>–<span class="number">31</span> <span class="number">14</span>:<span class="number">36</span>:<span class="number">04.844</span>: I/dalvikvm(<span class="number">389</span>): at shuliang.han.ndkhelloworld.MainActivity.onCreate(MainActivity.java:<span class="number">26</span>)
- <span class="number">01</span>–<span class="number">31</span> <span class="number">14</span>:<span class="number">36</span>:<span class="number">04.844</span>: I/dalvikvm(<span class="number">389</span>): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:<span class="number">1047</span>)
- <span class="number">01</span>–<span class="number">31</span> <span class="number">14</span>:<span class="number">36</span>:<span class="number">04.853</span>: I/dalvikvm(<span class="number">389</span>): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:<span class="number">1611</span>)
- <span class="number">01</span>–<span class="number">31</span> <span class="number">14</span>:<span class="number">36</span>:<span class="number">04.853</span>: I/dalvikvm(<span class="number">389</span>): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:<span class="number">1663</span>)
- <span class="number">01</span>–<span class="number">31</span> <span class="number">14</span>:<span class="number">36</span>:<span class="number">04.853</span>: I/dalvikvm(<span class="number">389</span>): at android.app.ActivityThread.access$<span class="number">1500</span>(ActivityThread.java:<span class="number">117</span>)
- <span class="number">01</span>–<span class="number">31</span> <span class="number">14</span>:<span class="number">36</span>:<span class="number">04.864</span>: I/dalvikvm(<span class="number">389</span>): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:<span class="number">931</span>)
- <span class="number">01</span>–<span class="number">31</span> <span class="number">14</span>:<span class="number">36</span>:<span class="number">04.864</span>: I/dalvikvm(<span class="number">389</span>): at android.os.Handler.dispatchMessage(Handler.java:<span class="number">99</span>)
- <span class="number">01</span>–<span class="number">31</span> <span class="number">14</span>:<span class="number">36</span>:<span class="number">04.864</span>: I/dalvikvm(<span class="number">389</span>): at android.os.Looper.loop(Looper.java:<span class="number">123</span>)
- <span class="number">01</span>–<span class="number">31</span> <span class="number">14</span>:<span class="number">36</span>:<span class="number">04.864</span>: I/dalvikvm(<span class="number">389</span>): at android.app.ActivityThread.main(ActivityThread.java:<span class="number">3683</span>)
- <span class="number">01</span>–<span class="number">31</span> <span class="number">14</span>:<span class="number">36</span>:<span class="number">04.864</span>: I/dalvikvm(<span class="number">389</span>): at java.lang.reflect.Method.invokeNative(Native Method)
- <span class="number">01</span>–<span class="number">31</span> <span class="number">14</span>:<span class="number">36</span>:<span class="number">04.874</span>: I/dalvikvm(<span class="number">389</span>): at java.lang.reflect.Method.invoke(Method.java:<span class="number">507</span>)
- <span class="number">01</span>–<span class="number">31</span> <span class="number">14</span>:<span class="number">36</span>:<span class="number">04.874</span>: I/dalvikvm(<span class="number">389</span>): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:<span class="number">839</span>)
- <span class="number">01</span>–<span class="number">31</span> <span class="number">14</span>:<span class="number">36</span>:<span class="number">04.874</span>: I/dalvikvm(<span class="number">389</span>): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:<span class="number">597</span>)
- <span class="number">01</span>–<span class="number">31</span> <span class="number">14</span>:<span class="number">36</span>:<span class="number">04.874</span>: I/dalvikvm(<span class="number">389</span>): at dalvik.system.NativeStart.main(Native Method)
- <span class="number">01</span>–<span class="number">31</span> <span class="number">14</span>:<span class="number">36</span>:<span class="number">04.884</span>: E/dalvikvm(<span class="number">389</span>): VM aborting
</div>
</div>
<div>
.
</div>
<div>
</div>
<div>
</div>
## <a name="t33"></a>4. JNIEnv 详解
<div>
</div>
<div>
**JNIEnv作用** : JNIEnv 是一个指针,**指向了一组JNI函数**, 这些函数可以在jni.h中查询到,**通过这些函数可以实现 Java层 与 JNI层的交互** , 通过JNIEnv 调用JNI函数 可以访问java虚拟机, 操作java对象;
</div>
<div>
</div>
<div>
**JNI线程相关性** : JNIEnv只在当前的线程有效,**JNIEnv不能跨线程传递**, 相同的Java线程调用本地方法, 所使用的JNIEnv是相同的, 一个Native方法不能被不同的Java线程调用;
</div>
<div>
</div>
<div>
**JNIEnv结构体系** : JNIEnv指针**指向一个线程相关的结构**,**线程相关结构指向一个指针数组**,**指针数组中的每个元素最终指向一个JNI函数**.
</div>
<div>
</div>
### <a name="t34"></a>(1) JNIEnv的C/C++声明
<div>
</div>
<div>
**jni.h中声明JNIEnv** : C语言中定义的JNIEnv 是 JNINativeInterface* , C++中定义的JNIEnv 是 _JNIEnv;
</div>
<div>
<div class="dp-highlighter bg_cpp">
<div class="bar">
<div class="tools">
**[cpp]** [view plain](http://blog.csdn.net/shulianghan/article/details/18964835#)[copy](http://blog.csdn.net/shulianghan/article/details/18964835#)[](https://code.csdn.net/snippets/181707)[](https://code.csdn.net/snippets/181707/fork)
<div>
<embed id="ZeroClipboardMovie_17" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_17">
</embed>
</div>
</div>
</div>
- <span class="keyword">struct</span> _JNIEnv;
- <span class="keyword">struct</span> _JavaVM;
- <span class="keyword">typedef</span> <span class="keyword">const</span> <span class="keyword">struct</span> JNINativeInterface* C_JNIEnv;
-
- <span class="preprocessor">#if defined(__cplusplus) //为了兼容C 和 C++两种代码 使用该 宏加以区分</span>
- <span class="keyword">typedef</span> _JNIEnv JNIEnv; <span class="comment">//C++ 中的JNIEnv类型</span>
- <span class="keyword">typedef</span> _JavaVM JavaVM;
- <span class="preprocessor">#else</span>
- <span class="keyword">typedef</span> <span class="keyword">const</span> <span class="keyword">struct</span> JNINativeInterface* JNIEnv;<span class="comment">//C语言中的JNIEnv类型</span>
- <span class="keyword">typedef</span> <span class="keyword">const</span> <span class="keyword">struct</span> JNIInvokeInterface* JavaVM;
- <span class="preprocessor">#endif</span>
</div>
### <a name="t35"></a>(2) C语言中的JNIEnv
</div>
<div>
</div>
<div>
**关于JNIEnv指针调用解析** : C中**JNIEnv就是 const struct JNINativeInterface***, JNIEnv * env**等价于 JNINativeInterface** env**, 因此要得到JNINativeInterface结构体中定义的函数指针, 就必须先获取到 JNINativeInterface的一级指针对象 即 *env , 该**一级指针对象就是 JNINativeInterface* env**, 然后通过该**一级指针对象调用JNI函数 : (*env)->NewStringUTF(env, “hello”)**;
</div>
<div>
</div>
<div>
**在JNINativeInterface结构体中定义了一系列的关于Java操作的相关方法** :
</div>
<div>
<div class="dp-highlighter bg_cpp">
<div class="bar">
<div class="tools">
**[cpp]** [view plain](http://blog.csdn.net/shulianghan/article/details/18964835#)[copy](http://blog.csdn.net/shulianghan/article/details/18964835#)[](https://code.csdn.net/snippets/181707)[](https://code.csdn.net/snippets/181707/fork)
<div>
<embed id="ZeroClipboardMovie_18" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_18">
</embed>
</div>
</div>
</div>
- <span class="comment">/*</span>
- <span class="comment"> * Table of interface function pointers.</span>
- <span class="comment"> */</span>
- <span class="keyword">struct</span> JNINativeInterface {
- <span class="keyword">void</span>* reserved0;
- <span class="keyword">void</span>* reserved1;
-
- … …
-
- jboolean (*CallStaticBooleanMethodV)(JNIEnv*, jclass, jmethodID,
- <span class="datatypes">va_list</span>);
- jboolean (*CallStaticBooleanMethodA)(JNIEnv*, jclass, jmethodID,
- jvalue*);
- jbyte (*CallStaticByteMethod)(JNIEnv*, jclass, jmethodID, …);
- jbyte (*CallStaticByteMethodV)(JNIEnv*, jclass, jmethodID, <span class="datatypes">va_list</span>);
-
- … …
-
- <span class="keyword">void</span>* (*GetDirectBufferAddress)(JNIEnv*, jobject);
- jlong (*GetDirectBufferCapacity)(JNIEnv*, jobject);
-
- <span class="comment">/* added in JNI 1.6 */</span>
- jobjectRefType (*GetObjectRefType)(JNIEnv*, jobject);
- };
</div>
### <a name="t36"></a>(3) C++中的JNIEnv
</div>
<div>
</div>
<div>
**C++ 中的JNIEnv**: C++ 中的JNIEnv 就是 _JNIEnv 结构体, 二者是等同的; 因此在调用 JNI函数的时候, 只需要使用 env->NewStringUTF(env, “hello”)方法即可, 不用在进行*运算;
</div>
<div>
</div>
<div>
.
</div>
<div>
<div class="dp-highlighter bg_cpp">
<div class="bar">
<div class="tools">
**[cpp]** [view plain](http://blog.csdn.net/shulianghan/article/details/18964835#)[copy](http://blog.csdn.net/shulianghan/article/details/18964835#)[](https://code.csdn.net/snippets/181707)[](https://code.csdn.net/snippets/181707/fork)
<div>
<embed id="ZeroClipboardMovie_19" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_19">
</embed>
</div>
</div>
</div>
- <span class="comment">/*</span>
- <span class="comment"> * C++ object wrapper.</span>
- <span class="comment"> *</span>
- <span class="comment"> * This is usually overlaid on a C struct whose first element is a</span>
- <span class="comment"> * JNINativeInterface*. We rely somewhat on compiler behavior.</span>
- <span class="comment"> */</span>
- <span class="keyword">struct</span> _JNIEnv {
- <span class="comment">/* do not rename this; it does not seem to be entirely opaque */</span>
- <span class="keyword">const</span> <span class="keyword">struct</span> JNINativeInterface* functions;
-
- <span class="preprocessor">#if defined(__cplusplus)</span>
-
- jint GetVersion()
- { <span class="keyword">return</span> functions->GetVersion(<span class="keyword">this</span>); }
-
- jlong GetDirectBufferCapacity(jobject buf)
- { <span class="keyword">return</span> functions->GetDirectBufferCapacity(<span class="keyword">this</span>, buf); }
-
- <span class="comment">/* added in JNI 1.6 */</span>
- jobjectRefType GetObjectRefType(jobject obj)
- { <span class="keyword">return</span> functions->GetObjectRefType(<span class="keyword">this</span>, obj); }
- <span class="preprocessor">#endif /*__cplusplus*/</span>
- };
</div>
## <a name="t37"></a>5. JNI方法命名规则(标准JNI规范)
</div>
<div>
</div>
<div>
**JNI实现的方法 与 Java中Native方法的映射关系** : 使用方法名进行映射, 可以使用 javah 工具进入 bin/classes 目录下执行命令, 即可生成头文件;
</div>
<div>
</div>
<div>
**JNI方法参数介绍**:
</div>
<div>
— **参数①** : 第一个参数是JNI接口指针 JNIEnv;
</div>
<div>
— **参数②** : 如果Native方法是非静态的, 那么第二个参数就是对Java对象的引用, 如果Native方法是静态的, 那么第二个参数就是对Java类的Class对象的引用;
</div>
<div>
</div>
<div>
**JNI方法名规范** : 返回值 + Java前缀 + 全路径类名 + 方法名 + 参数① JNIEnv + 参数② jobject + 其它参数;
</div>
<div>
— **注意分隔符** : Java前缀 与 类名 以及类名之间的包名 和 方法名之间 使用 “_” 进行分割;
</div>
<div>
</div>
<div>
**声明 非静态 方法**:
</div>
<div>
— **Native方法** : public int hello (String str, int i);
</div>
<div>
— **JNI方法**: jint Java_shuliang_han_Hello_hello(JNIEnv * env, jobject obj, jstring str, jint i);
</div>
<div>
</div>
<div>
**声明 静态 方法** :
</div>
<div>
— **Native方法** : public static int hello (String str, int i);
</div>
<div>
—**JNI方法** : jint Java_shuliang_han_Hello_hello(JNIEnv * env, jobject clazz, jstring str, jint i);
</div>
<div>
</div>
<div>
**两种规范** : 以上是Java的标准JNI规范, 在Android中还有一套自定义的规范, 该规范是Android应用框架层 和 框架层交互使用的JNI规范, 依靠方法注册 映射 Native方法 和 JNI方法;
</div>
<div>
</div>
## <a name="t38"></a>6. JNI方法签名规则
<div>
</div>
<div>
**JNI识别Java方法** : **JNI依靠函数名 和 方法签名 识别方法**, 函数名是不能唯一识别一个方法的, 因为方法可以重载, 类型签名代表了 参数 和 返回值;
</div>
<div>
— **签名规则** : (**参数1类型签名参数2类型签名参数3类型签名参数N类型签名**…)**返回值类型签名**, 注意参数列表中没有任何间隔;
</div>
<div>
</div>
<div>
**Java类型 与 类型签名对照表** : 注意 boolean 与 long 不是大写首字母, 分别是 Z 与 J, 类是L全限定类名, 数组是[元素类型签名;
</div>
<div>
— **类的签名规则** :**L + 全限定名 + ;**三部分, 全限定类名以 / 分割;
</div>
<div>
<table border="1" width="500" cellspacing="1" cellpadding="1">
<tr>
<td>
Java类型
</td>
<td>
类型签名
</td>
</tr>
<tr>
<td>
boolean
</td>
<td>
Z
</td>
</tr>
<tr>
<td>
byte
</td>
<td>
B
</td>
</tr>
<tr>
<td>
char
</td>
<td>
C
</td>
</tr>
<tr>
<td>
short
</td>
<td>
S
</td>
</tr>
<tr>
<td>
int
</td>
<td>
I
</td>
</tr>
<tr>
<td>
long
</td>
<td>
J
</td>
</tr>
<tr>
<td>
float
</td>
<td>
F
</td>
</tr>
<tr>
<td>
double
</td>
<td>
D
</td>
</tr>
<tr>
<td>
类
</td>
<td>
L全限定类名
</td>
</tr>
<tr>
<td>
数组
</td>
<td>
[元素类型签名
</td>
</tr>
</table>
</div>
<div>
</div>
<div>
eg. long function(int n, String str, int[] arr);
</div>
<div>
**该方法的签名** :**(ILjava/lang/String;[I)J**
</div>
<div>
.
</div>
<div>
</div>
<div>
.
</div>
<div>
</div>
# <a name="t39"></a>四. Java调用JNI法与日志打印
<div>
</div>
<div>
</div>
## <a name="t40"></a>1. JNI数据类型
<div>
</div>
<div>
</div>
<div>
**Java数据类型 C数据类型 JNI数据类型对比** : 32位 与 64位机器可能会有出入;
</div>
<div>
</div>
<div>
<table border="1" width="600" cellspacing="1" cellpadding="1">
<tr>
<td>
**Java数据类型**
</td>
<td>
**C本地类型**
</td>
<td>
**JNI定义别名**
</td>
</tr>
<tr>
<td>
int
</td>
<td>
long
</td>
<td>
jint/jsize
</td>
</tr>
<tr>
<td>
long
</td>
<td>
__int64
</td>
<td>
jlong
</td>
</tr>
<tr>
<td>
byte
</td>
<td>
signed char
</td>
<td>
jbyte
</td>
</tr>
<tr>
<td>
boolean
</td>
<td>
unsigned char
</td>
<td>
jboolean
</td>
</tr>
<tr>
<td>
char
</td>
<td>
unsigned short
</td>
<td>
jchar
</td>
</tr>
<tr>
<td>
short
</td>
<td>
short
</td>
<td>
jshort
</td>
</tr>
<tr>
<td>
float
</td>
<td>
float
</td>
<td>
jfloat
</td>
</tr>
<tr>
<td>
double
</td>
<td>
doyble
</td>
<td>
jdouble
</td>
</tr>
<tr>
<td>
object’
</td>
<td>
_jobject
</td>
<td>
jobject
</td>
</tr>
</table>
</div>
<div>
**数据类型表示方法** : int数组类型 jintArray , boolean数组 jbooleanArray …
</div>
<div>
</div>
<div>
**头文件定义类型** : 这些基本的数据类型在**jni.h 中都有相应的定义** :
</div>
<div>
<div class="dp-highlighter bg_cpp">
<div class="bar">
<div class="tools">
**[cpp]** [view plain](http://blog.csdn.net/shulianghan/article/details/18964835#)[copy](http://blog.csdn.net/shulianghan/article/details/18964835#)[](https://code.csdn.net/snippets/181707)[](https://code.csdn.net/snippets/181707/fork)
<div>
<embed id="ZeroClipboardMovie_20" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_20">
</embed>
</div>
</div>
</div>
- jobject (*CallObjectMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
- jboolean (*CallBooleanMethod)(JNIEnv*, jobject, jmethodID, …);
- jboolean (*CallBooleanMethodV)(JNIEnv*, jobject, jmethodID, <span class="datatypes">va_list</span>);
- jboolean (*CallBooleanMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
- jbyte (*CallByteMethod)(JNIEnv*, jobject, jmethodID, …);
- jbyte (*CallByteMethodV)(JNIEnv*, jobject, jmethodID, <span class="datatypes">va_list</span>);
- jbyte (*CallByteMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
- jchar (*CallCharMethod)(JNIEnv*, jobject, jmethodID, …);
- jchar (*CallCharMethodV)(JNIEnv*, jobject, jmethodID, <span class="datatypes">va_list</span>);
- jchar (*CallCharMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
- jshort (*CallShortMethod)(JNIEnv*, jobject, jmethodID, …);
- jshort (*CallShortMethodV)(JNIEnv*, jobject, jmethodID, <span class="datatypes">va_list</span>);
- jshort (*CallShortMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
- jint (*CallIntMethod)(JNIEnv*, jobject, jmethodID, …);
- jint (*CallIntMethodV)(JNIEnv*, jobject, jmethodID, <span class="datatypes">va_list</span>);
- jint (*CallIntMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
- jlong (*CallLongMethod)(JNIEnv*, jobject, jmethodID, …);
- jlong (*CallLongMethodV)(JNIEnv*, jobject, jmethodID, <span class="datatypes">va_list</span>);
- jbooleanArray (*NewBooleanArray)(JNIEnv*, jsize);
- jbyteArray (*NewByteArray)(JNIEnv*, jsize);
- jcharArray (*NewCharArray)(JNIEnv*, jsize);
- jshortArray (*NewShortArray)(JNIEnv*, jsize);
- jintArray (*NewIntArray)(JNIEnv*, jsize);
- jlongArray (*NewLongArray)(JNIEnv*, jsize);
- jfloatArray (*NewFloatArray)(JNIEnv*, jsize);
- jdoubleArray (*NewDoubleArray)(JNIEnv*, jsize);
</div>
</div>
## <a name="t41"></a>2. JNI在Java和C语言之间传递int类型
<div>
</div>
<div>
</div>
<div>
**Java中定义的方法** :
</div>
<div>
<div class="dp-highlighter bg_java">
<div class="bar">
<div class="tools">
**[java]** [view plain](http://blog.csdn.net/shulianghan/article/details/18964835#)[copy](http://blog.csdn.net/shulianghan/article/details/18964835#)[](https://code.csdn.net/snippets/181707)[](https://code.csdn.net/snippets/181707/fork)
<div>
<embed id="ZeroClipboardMovie_21" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_21">
</embed>
</div>
</div>
</div>
- <span class="comment">//将Java中的两个int值 传给C语言, 进行相加后, 返回java语言 shuliang.han.ndkparameterpassing.DataProvider</span>
- <span class="keyword">public</span> <span class="keyword">native</span> <span class="keyword">int</span> add(<span class="keyword">int</span> x, <span class="keyword">int</span> y);
</div>
**C语言中定义的方法** :</div>
<div>
<div class="dp-highlighter bg_cpp">
<div class="bar">
<div class="tools">
**[cpp]** [view plain](http://blog.csdn.net/shulianghan/article/details/18964835#)[copy](http://blog.csdn.net/shulianghan/article/details/18964835#)[](https://code.csdn.net/snippets/181707)[](https://code.csdn.net/snippets/181707/fork)
<div>
<embed id="ZeroClipboardMovie_22" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_22">
</embed>
</div>
</div>
</div>
- <span class="preprocessor">#include <jni.h></span>
-
- <span class="comment">//方法签名, Java环境 和 调用native方法的类 必不可少, 后面的参数就是native方法的参数</span>
- jint Java_shuliang_han_ndkparameterpassing_DataProvider_add(JNIEnv * env, jobject obj, jint x, jint y)
- {
- <span class="keyword">return</span> x + y;
- }
</div>
**使用NDK工具变异该c类库** :</div>
<div>
在cygwin中进入cygdrive, 然后**进入windows中相应的目录**, 执行 **/android-ndk-r9c/ndk-build** 命令, 即可完成编译;
</div>
<div>

</div>
<div>
</div>
<div>
</div>
## <a name="t42"></a>3. NDK中C代码使用LogCat
<div>
</div>
<div>
</div>
### <a name="t43"></a>(1) 引入头文件
<div>
</div>
<div>
**NDK中断点调试** : 断点调试在NDK中实现极其困难, 因此在这里我们一般都是打印日志;
</div>
<div>
</div>
<div>
**引入头文件** : 在C代码中引入下面的头文件;
</div>
<div>
<div class="dp-highlighter bg_cpp">
<div class="bar">
<div class="tools">
**[cpp]** [view plain](http://blog.csdn.net/shulianghan/article/details/18964835#)[copy](http://blog.csdn.net/shulianghan/article/details/18964835#)[](https://code.csdn.net/snippets/181707)[](https://code.csdn.net/snippets/181707/fork)
<div>
<embed id="ZeroClipboardMovie_23" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_23">
</embed>
</div>
</div>
</div>
- <span class="preprocessor">#include <android/log.h></span>
- <span class="preprocessor">#define LOG_TAG “System.out”</span>
- <span class="preprocessor">#define LOGD(…) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)</span>
- <span class="preprocessor">#define LOGI(…) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)</span>
</div>
**头文件介绍** : log.h 是关于调用 LogCat日志文件;</div>
<div>
— **log.h头文件路径** : android-ndk-r9c\platforms\android-9\arch-arm\usr\include\android\log.h;
</div>
<div>
— **主要方法** : __android_log_write, 下面有该方法的解析, 传入参数 日志等级 日志标签 日志内容;
</div>
<div>
— **宏定义** : __android_log_write 方法太麻烦, 这里做出一个映射, LOGD(…) 输出debug级别的日志, LOGI(…) 输出Info级别的日志;
</div>
<div>
—**LogCat日志级别** : verbose < debug < info < warn < error < assert;
</div>
<div>
</div>
<div>
**使用到的log.h文件内容解析** : __android_log_write 方法中的日志等级参数就使用 枚举中的内容
</div>
<div>
<div class="dp-highlighter bg_cpp">
<div class="bar">
<div class="tools">
**[cpp]** [view plain](http://blog.csdn.net/shulianghan/article/details/18964835#)[copy](http://blog.csdn.net/shulianghan/article/details/18964835#)[](https://code.csdn.net/snippets/181707)[](https://code.csdn.net/snippets/181707/fork)
<div>
<embed id="ZeroClipboardMovie_24" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_24">
</embed>
</div>
</div>
</div>
- <span class="comment">/*</span>
- <span class="comment"> * Android log priority values, in ascending priority order. 日志等级</span>
- <span class="comment"> */</span>
- <span class="keyword">typedef</span> <span class="keyword">enum</span> android_LogPriority {
- ANDROID_LOG_UNKNOWN = 0,
- ANDROID_LOG_DEFAULT, <span class="comment">/* only for SetMinPriority() */</span>
- ANDROID_LOG_VERBOSE,
- ANDROID_LOG_DEBUG,
- ANDROID_LOG_INFO,
- ANDROID_LOG_WARN,
- ANDROID_LOG_ERROR,
- ANDROID_LOG_FATAL,
- ANDROID_LOG_SILENT, <span class="comment">/* only for SetMinPriority(); must be last */</span>
- } android_LogPriority;
-
- <span class="comment">/*</span>
- <span class="comment"> * Send a simple string to the log. 向LogCat中输出日志 </span>
- <span class="comment"> 参数介绍: 日志优先级 , 日志标签 , 日志内容</span>
- <span class="comment"> */</span>
- <span class="datatypes">int</span> __android_log_write(<span class="datatypes">int</span> prio, <span class="keyword">const</span> <span class="datatypes">char</span> *tag, <span class="keyword">const</span> <span class="datatypes">char</span> *text);
</div>
**C语言中输入输出函数占位符介绍** :</div>
<div>
<table border="1" width="200" cellspacing="1" cellpadding="1">
<tr>
<td>
占位符
</td>
<td>
数据类型
</td>
</tr>
<tr>
<td>
%d
</td>
<td>
int
</td>
</tr>
<tr>
<td>
%ld
</td>
<td>
long int
</td>
</tr>
<tr>
<td>
%c
</td>
<td>
char
</td>
</tr>
<tr>
<td>
%f
</td>
<td>
float
</td>
</tr>
<tr>
<td>
&lf
</td>
<td>
double
</td>
</tr>
<tr>
<td>
%x
</td>
<td>
十六进制
</td>
</tr>
<tr>
<td>
%O
</td>
<td>
八进制
</td>
</tr>
<tr>
<td>
%s
</td>
<td>
字符串
</td>
</tr>
</table>
</div>
<div>
.
</div>
<div>
.
</div>
<div>
</div>
### <a name="t44"></a>(2) Android.mk增加liblog.so动态库
<div>
</div>
<div>
**在该make配置文件中, 增加一行** : LOCAL_LDLIBS += -llog , 该语句添加在 LOCAL_SRC_FILES 语句下面一行;
</div>
<div>
</div>
<div>
**完整的Android.mk文件** :
</div>
<div>
<div class="dp-highlighter bg_plain">
<div class="bar">
<div class="tools">
**[plain]** [view plain](http://blog.csdn.net/shulianghan/article/details/18964835#)[copy](http://blog.csdn.net/shulianghan/article/details/18964835#)[](https://code.csdn.net/snippets/181707)[](https://code.csdn.net/snippets/181707/fork)
<div>
<embed id="ZeroClipboardMovie_25" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_25">
</embed>
</div>
</div>
</div>
- LOCAL_PATH := $(call my-dir)
-
- include $(CLEAR_VARS)
-
- LOCAL_MODULE := DataProvider
- LOCAL_SRC_FILES := DataProvider.c
- #增加log函数对应的函数库 liblog.so libthread_db.a
- LOCAL_LDLIBS += -llog -lthread_db
- include $(BUILD_SHARED_LIBRARY)
</div>
</div>
<div>
**函数库位置** : android-ndk-r9c\platforms\android-9\arch-arm\usr\lib;
</div>
<div>
**函数库截图** : 从该目录下的 liglog.so可以看出, 存在该库;
</div>
<div>

</div>
<div>
**引入函数库方法** : 使用 LOCAL_LDLIBS += -l函数库名, 注意**函数库名不带lib前缀** 和**.so 后缀**, 同时可以添加多个库, 使用 -l库1 -l库2 -库3 ;
</div>
<div>
</div>
<div>
</div>
### <a name="t45"></a>(3) 编译执行
<div>
</div>
<div>
**根据(1) 中的占位符, 编写打印日志代码**:
</div>
<div>
<div class="dp-highlighter bg_cpp">
<div class="bar">
<div class="tools">
**[cpp]** [view plain](http://blog.csdn.net/shulianghan/article/details/18964835#)[copy](http://blog.csdn.net/shulianghan/article/details/18964835#)[](https://code.csdn.net/snippets/181707)[](https://code.csdn.net/snippets/181707/fork)
<div>
<embed id="ZeroClipboardMovie_26" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_26">
</embed>
</div>
</div>
</div>
- <span class="comment">//Java中的int对应的是C语言中的long类型, 对应JNI中的jint类型, C语言中</span>
- LOGI(<span class="string">“JNI_日志 : x = %ld , y = %ld”</span> , x , y);
</div>
**最终的包含打印日志的完整代码** : 注意, 这里有一处可能错误, 如果是32位机器, int类型占位符使用 %d 即可;</div>
<div>
<div class="dp-highlighter bg_cpp">
<div class="bar">
<div class="tools">
**[cpp]** [view plain](http://blog.csdn.net/shulianghan/article/details/18964835#)[copy](http://blog.csdn.net/shulianghan/article/details/18964835#)[](https://code.csdn.net/snippets/181707)[](https://code.csdn.net/snippets/181707/fork)
<div>
<embed id="ZeroClipboardMovie_27" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_27">
</embed>
</div>
</div>
</div>
- <span class="preprocessor">#include <jni.h></span>
- <span class="preprocessor">#include <android/log.h></span>
- <span class="preprocessor">#define LOG_TAG “System.out”</span>
- <span class="preprocessor">#define LOGD(…) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)</span>
- <span class="preprocessor">#define LOGI(…) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)</span>
-
-
- <span class="comment">//方法签名, Java环境 和 调用native方法的类 必不可少, 后面的参数就是native方法的参数</span>
- jint Java_shuliang_han_ndkparameterpassing_DataProvider_add(JNIEnv * env, jobject obj, jint x, jint y)
- {
- <span class="comment">//Java中的int对应的是C语言中的long类型, 对应JNI中的jint类型, C语言中</span>
- LOGI(<span class="string">“JNI_日志 : x = %ld , y = %ld”</span> , x , y);
- <span class="keyword">return</span> x + y;
- }
</div>
**重新编译C文件** : 执行 **/android-ndk-r9c/ndk-build**命令;</div>
<div>
— **第一次编译** : 出现警告, long int占位符行不通, 注意区分机器位长, 64位 与 32位不同, 这样编译出现的结果就不会打印日志;
</div>
<div>

</div>
<div>
— **第二次编译** : 将占位符改为 %d ;
</div>
<div>

</div>
<div>
</div>
<div>
**执行按钮之后打印的日志** : 虽然有乱码, 不过显示出来了;
</div>
<div>

</div>
<div>
</div>
<div>

</div>
<div>
</div>
<div>
</div>
## <a name="t46"></a>4. 字符串处理
<div>
.
</div>
<div>
</div>
<div>
**Java中的String转为C语言中的char字符串** : 下面的工具方法可以在C程序中解决这个问题;
</div>
<div>
<div class="dp-highlighter bg_cpp">
<div class="bar">
<div class="tools">
**[cpp]** [view plain](http://blog.csdn.net/shulianghan/article/details/18964835#)[copy](http://blog.csdn.net/shulianghan/article/details/18964835#)[](https://code.csdn.net/snippets/181707)[](https://code.csdn.net/snippets/181707/fork)
<div>
<embed id="ZeroClipboardMovie_28" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_28">
</embed>
</div>
</div>
</div>
- <span class="comment">// java中的jstring, 转化为c的一个字符数组</span>
- <span class="datatypes">char</span>* Jstring2CStr(JNIEnv* env, jstring jstr) {
- <span style=<span class="string">“white-space:pre”</span>> </span><span class="comment">//声明了一个字符串变量 rtn</span>
- <span style=<span class="string">“white-space:pre”</span>> </span><span class="datatypes">char</span>* rtn = NULL;
- <span style=<span class="string">“white-space:pre”</span>> </span><span class="comment">//找到Java中的String的Class对象</span>
- <span style=<span class="string">“white-space:pre”</span>> </span>jclass clsstring = (*env)->FindClass(env, <span class="string">“java/lang/String”</span>);
- <span style=<span class="string">“white-space:pre”</span>> </span><span class="comment">//创建一个Java中的字符串 “GB2312”</span>
- <span style=<span class="string">“white-space:pre”</span>> </span>jstring strencode = (*env)->NewStringUTF(env, <span class="string">“GB2312”</span>);
- <span style=<span class="string">“white-space:pre”</span>> </span><span class="comment">/*</span>
- <span class="comment"><span style=”white-space:pre”> </span> * 获取String中定义的方法 getBytes(), 该方法的参数是 String类型的, 返回值是 byte[]数组</span>
- <span class="comment"><span style=”white-space:pre”> </span> * “(Ljava/lang/String;)[B” 方法前面解析 :</span>
- <span class="comment"><span style=”white-space:pre”> </span> * — Ljava/lang/String; 表示参数是String字符串</span>
- <span class="comment"><span style=”white-space:pre”> </span> * — [B : 中括号表示这是一个数组, B代表byte类型, 返回值是一个byte数组</span>
- <span class="comment"><span style=”white-space:pre”> </span> */</span>
- <span style=<span class="string">“white-space:pre”</span>> </span>jmethodID mid = (*env)->GetMethodID(env, clsstring, <span class="string">“getBytes”</span>,
- <span style=<span class="string">“white-space:pre”</span>> </span><span class="string">“(Ljava/lang/String;)[B”</span>);
- <span style=<span class="string">“white-space:pre”</span>> </span><span class="comment">//调用Java中的getBytes方法, 传入参数介绍 参数②表示调用该方法的对象, 参数③表示方法id , 参数④表示方法参数</span>
- <span style=<span class="string">“white-space:pre”</span>> </span>jbyteArray barr = (jbyteArray)(*env)->CallObjectMethod(env, jstr, mid,
- <span style=<span class="string">“white-space:pre”</span>> </span>strencode); <span class="comment">// String .getByte(“GB2312”);</span>
- <span style=<span class="string">“white-space:pre”</span>> </span><span class="comment">//获取数组的长度</span>
- <span style=<span class="string">“white-space:pre”</span>> </span>jsize alen = (*env)->GetArrayLength(env, barr);
- <span style=<span class="string">“white-space:pre”</span>> </span><span class="comment">//获取数组中的所有的元素 , 存放在 jbyte*数组中</span>
- <span style=<span class="string">“white-space:pre”</span>> </span>jbyte* ba = (*env)->GetByteArrayElements(env, barr, JNI_FALSE);
- <span style=<span class="string">“white-space:pre”</span>> </span><span class="comment">//将Java数组中所有元素拷贝到C的char*数组中, 注意C语言数组结尾要加一个 ‘\0’</span>
- <span style=<span class="string">“white-space:pre”</span>> </span><span class="keyword">if</span> (alen > 0) {
- <span style=<span class="string">“white-space:pre”</span>> </span>rtn = (<span class="datatypes">char</span>*) malloc(alen + 1); <span class="comment">//new char[alen+1]; “\0”</span>
- <span style=<span class="string">“white-space:pre”</span>> </span>memcpy(rtn, ba, alen);
- <span style=<span class="string">“white-space:pre”</span>> </span>rtn[alen] = 0;
- <span style=<span class="string">“white-space:pre”</span>> </span>}
- <span style=<span class="string">“white-space:pre”</span>> </span>(*env)->ReleaseByteArrayElements(env, barr, ba, 0); <span class="comment">//释放内存</span>
-
-
- <span style=<span class="string">“white-space:pre”</span>> </span><span class="keyword">return</span> rtn;
- }
</div>
<div>
</div>
<div>
**Jstring2CStr方法讲解** :
</div>
<div>
a. **获取Java中String类型的class对象** : 参数 : 上下文环境 env, String类完整路径 ;
</div>
<div>
<div class="dp-highlighter bg_cpp">
<div class="bar">
<div class="tools">
**[cpp]** [view plain](http://blog.csdn.net/shulianghan/article/details/18964835#)[copy](http://blog.csdn.net/shulianghan/article/details/18964835#)[](https://code.csdn.net/snippets/181707)[](https://code.csdn.net/snippets/181707/fork)
<div>
<embed id="ZeroClipboardMovie_29" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_29">
</embed>
</div>
</div>
</div>
- jclass clsstring = (*env)->FindClass(env, <span class="string">“java/lang/String”</span>);
</div>
b.**创建Java字符串** : 使用 NewStringUTF 方法;</div>
<div>
<div class="dp-highlighter bg_cpp">
<div class="bar">
<div class="tools">
**[cpp]** [view plain](http://blog.csdn.net/shulianghan/article/details/18964835#)[copy](http://blog.csdn.net/shulianghan/article/details/18964835#)[](https://code.csdn.net/snippets/181707)[](https://code.csdn.net/snippets/181707/fork)
<div>
<embed id="ZeroClipboardMovie_30" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_30">
</embed>
</div>
</div>
</div>
- jstring strencode = (*env)->NewStringUTF(env, <span class="string">“GB2312”</span>);
</div>
c.**获取String中的getBytes()方法** : 参数介绍 ① env 上下文环境 ② 完整的类路径 ③ 方法名 ④ 方法签名, 方法签名 Ljava/lang/String; 代表参数是String字符串, [B 中括号表示这是一个数组, B代表byte类型, 返回值是一个byte数组;</div>
<div>
<div class="dp-highlighter bg_cpp">
<div class="bar">
<div class="tools">
**[cpp]** [view plain](http://blog.csdn.net/shulianghan/article/details/18964835#)[copy](http://blog.csdn.net/shulianghan/article/details/18964835#)[](https://code.csdn.net/snippets/181707)[](https://code.csdn.net/snippets/181707/fork)
<div>
<embed id="ZeroClipboardMovie_31" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_31">
</embed>
</div>
</div>
</div>
- jmethodID mid = (*env)->GetMethodID(env, clsstring, <span class="string">“getBytes”</span>,
- <span class="string">“(Ljava/lang/String;)[B”</span>);
</div>
d. **获取数组的长度** :</div>
<div>
<div class="dp-highlighter bg_cpp">
<div class="bar">
<div class="tools">
**[cpp]** [view plain](http://blog.csdn.net/shulianghan/article/details/18964835#)[copy](http://blog.csdn.net/shulianghan/article/details/18964835#)[](https://code.csdn.net/snippets/181707)[](https://code.csdn.net/snippets/181707/fork)
<div>
<embed id="ZeroClipboardMovie_32" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_32">
</embed>
</div>
</div>
</div>
- jsize alen = (*env)->GetArrayLength(env, barr);
</div>
e. 获取数组元素 : 获取数组中的所有的元素 , 存放在 jbyte*数组中;
</div>
<div>
<div class="dp-highlighter bg_cpp">
<div class="bar">
<div class="tools">
**[cpp]** [view plain](http://blog.csdn.net/shulianghan/article/details/18964835#)[copy](http://blog.csdn.net/shulianghan/article/details/18964835#)[](https://code.csdn.net/snippets/181707)[](https://code.csdn.net/snippets/181707/fork)
<div>
<embed id="ZeroClipboardMovie_33" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_33">
</embed>
</div>
</div>
</div>
- jbyte* ba = (*env)->GetByteArrayElements(env, barr, JNI_FALSE);
</div>
f.**数组拷贝**: 将Java数组中所有元素拷贝到C的char*数组中, 注意C语言数组结尾要加一个 ‘\0’;</div>
<div>
<div class="dp-highlighter bg_cpp">
<div class="bar">
<div class="tools">
**[cpp]** [view plain](http://blog.csdn.net/shulianghan/article/details/18964835#)[copy](http://blog.csdn.net/shulianghan/article/details/18964835#)[](https://code.csdn.net/snippets/181707)[](https://code.csdn.net/snippets/181707/fork)
<div>
<embed id="ZeroClipboardMovie_34" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_34">
</embed>
</div>
</div>
</div>
- <span class="keyword">if</span> (alen > 0) {
- rtn = (<span class="datatypes">char</span>*) malloc(alen + 1); <span class="comment">//new char[alen+1]; “\0”</span>
- memcpy(rtn, ba, alen);
- rtn[alen] = 0;
- }
</div>
g.**释放内存** :</div>
<div>
<div class="dp-highlighter bg_cpp">
<div class="bar">
<div class="tools">
**[cpp]** [view plain](http://blog.csdn.net/shulianghan/article/details/18964835#)[copy](http://blog.csdn.net/shulianghan/article/details/18964835#)[](https://code.csdn.net/snippets/181707)[](https://code.csdn.net/snippets/181707/fork)
<div>
<embed id="ZeroClipboardMovie_35" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_35">
</embed>
</div>
</div>
</div>
- (*env)->ReleaseByteArrayElements(env, barr, ba, 0); <span class="comment">//释放内存</span>
</div>
</div>
<div>
</div>
<div>
.
**作者** : **万境绝尘 **
**转载请注明出处** : [**http://blog.csdn.net/shulianghan/article/details/18964835**](http://blog.csdn.net/shulianghan/article/details/18964835)
.
</div>
**C语言方法** : 注意调用Jstring2CStr方法之后要强转, 否则会出错, Jstring2CStr方法要定义在该方法的前面, C语言中的方法要先声明才能使用;</div>
<div>
<div class="dp-highlighter bg_cpp">
<div class="bar">
<div class="tools">
**[cpp]** [view plain](http://blog.csdn.net/shulianghan/article/details/18964835#)[copy](http://blog.csdn.net/shulianghan/article/details/18964835#)[](https://code.csdn.net/snippets/181707)[](https://code.csdn.net/snippets/181707/fork)
<div>
<embed id="ZeroClipboardMovie_36" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_36">
</embed>
</div>
</div>
</div>
- jstring Java_shuliang_han_ndkparameterpassing_DataProvider_sayHelloInc(JNIEnv *env, jobject obj, jstring str)
- {
- <span class="datatypes">char</span> *p = (<span class="datatypes">char</span>*)Jstring2CStr(env, str);
- <span class="comment">//打印Java传递过来的数据 </span>
- LOGI(<span class="string">“Java JNI string parameter is : %s”</span>, p);
-
- <span class="datatypes">char</span> *append = <span class="string">“append”</span>;
-
- <span class="comment">//strcat(dest, source) 函数可以将source字符串 添加到dest字符串后面 </span>
- <span class="keyword">return</span> (*env)->NewStringUTF(env, strcat(p, append));
- }
</div>
— **如果没有强转会出现下面的错误** : char *p = Jstring2CStr(env, str);
— **将Jstring2CStr方法定义在主方法下面会出现下面错误** :</div>
<div>
</div>
<div>

</div>
<div>
**Java源码** :
</div>
<div>
<div class="dp-highlighter bg_java">
<div class="bar">
<div class="tools">
**[java]** [view plain](http://blog.csdn.net/shulianghan/article/details/18964835#)[copy](http://blog.csdn.net/shulianghan/article/details/18964835#)[](https://code.csdn.net/snippets/181707)[](https://code.csdn.net/snippets/181707/fork)
<div>
<embed id="ZeroClipboardMovie_37" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_37">
</embed>
</div>
</div>
</div>
- <span class="keyword">case</span> R.id.sayHelloInc:
- Toast.makeText(getApplicationContext(), dataProvider.sayHelloInc(<span class="string">“Hello”</span>), Toast.LENGTH_LONG).show();
- <span class="keyword">break</span>;
</div>
**编译之后运行结果** :</div>
<div>
</div>
<div>

</div>
<div>
</div>
<div>

</div>
<div>
</div>
## <a name="t47"></a>5. 开发JNI程序流程
<div>
</div>
<div>
a. **C语言类库接口** : 存在C语言类库, 调用接口为login_server(char* address, char* username, char* password);
b. Java定义本地方法 : public native void LoginServer(String address, String user, String pwd);
<div>
c. **C语言JNI代码** : Java_包名_类名_LoginServer(JNIEnv* env, jobject obj, jstring address, jstring user, jstring pwd){…调C接口};
**注意跨语言字符串转换**: JNI方法中, 要将Java的String字符串转为C中的char*字符串;
**首先验证C码农提供的代码是否可用** : 验证该api是否可用, 在一个 int main() 函数中进行测试, 根据该测试代码查看方法执行相关的情况;</div>
<div>
</div>
## <a name="t48"></a>6. 数组参数处理
<div>
</div>
<div>
**模块讲解** : 在该模块中, Java语言传递一个int数组参数给C语言, C语言将这一组参数读取出来, 并且输出到Android的LogCat中, 这里涉及到了两个重要的JNI方法, 一个数**获取数组长度方法**, 一个是**获取数组中每个元素的方法**;
**获取数组长度方法** : jni中定义 – jsize (*GetArrayLength)(JNIEnv*, jarray);
创建数组相关方法 :
<div>
<div class="dp-highlighter bg_cpp">
<div class="bar">
<div class="tools">
**[cpp]** [view plain](http://blog.csdn.net/shulianghan/article/details/18964835#)[copy](http://blog.csdn.net/shulianghan/article/details/18964835#)[](https://code.csdn.net/snippets/181707)[](https://code.csdn.net/snippets/181707/fork)
<div>
<embed id="ZeroClipboardMovie_38" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_38">
</embed>
</div>
</div>
</div>
- jbooleanArray (*NewBooleanArray)(JNIEnv*, jsize);
- jbyteArray (*NewByteArray)(JNIEnv*, jsize);
- jcharArray (*NewCharArray)(JNIEnv*, jsize);
- jshortArray (*NewShortArray)(JNIEnv*, jsize);
- jintArray (*NewIntArray)(JNIEnv*, jsize);
- jlongArray (*NewLongArray)(JNIEnv*, jsize);
- jfloatArray (*NewFloatArray)(JNIEnv*, jsize);
- jdoubleArray (*NewDoubleArray)(JNIEnv*, jsize);
</div>
**获取数组元素相关方法** :</div>
<div>
<div class="dp-highlighter bg_cpp">
<div class="bar">
<div class="tools">
**[cpp]** [view plain](http://blog.csdn.net/shulianghan/article/details/18964835#)[copy](http://blog.csdn.net/shulianghan/article/details/18964835#)[](https://code.csdn.net/snippets/181707)[](https://code.csdn.net/snippets/181707/fork)
<div>
<embed id="ZeroClipboardMovie_39" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_39">
</embed>
</div>
</div>
</div>
- jboolean* (*GetBooleanArrayElements)(JNIEnv*, jbooleanArray, jboolean*);
- jbyte* (*GetByteArrayElements)(JNIEnv*, jbyteArray, jboolean*);
- jchar* (*GetCharArrayElements)(JNIEnv*, jcharArray, jboolean*);
- jshort* (*GetShortArrayElements)(JNIEnv*, jshortArray, jboolean*);
- jint* (*GetIntArrayElements)(JNIEnv*, jintArray, jboolean*);
- jlong* (*GetLongArrayElements)(JNIEnv*, jlongArray, jboolean*);
- jfloat* (*GetFloatArrayElements)(JNIEnv*, jfloatArray, jboolean*);
- jdouble* (*GetDoubleArrayElements)(JNIEnv*, jdoubleArray, jboolean*);
</div>
**C语言代码** :</div>
<div>
<div class="dp-highlighter bg_cpp">
<div class="bar">
<div class="tools">
**[cpp]** [view plain](http://blog.csdn.net/shulianghan/article/details/18964835#)[copy](http://blog.csdn.net/shulianghan/article/details/18964835#)[](https://code.csdn.net/snippets/181707)[](https://code.csdn.net/snippets/181707/fork)
<div>
<embed id="ZeroClipboardMovie_40" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_40">
</embed>
</div>
</div>
</div>
- jintArray Java_shuliang_han_ndkparameterpassing_DataProvider_intMethod(JNIEnv *env, jobject obj, jintArray arr)
- {
- <span class="comment">//获取arr大小 </span>
- <span class="datatypes">int</span> len = (*env)->GetArrayLength(env, arr);
-
- <span class="comment">//在LogCat中打印出arr的大小 </span>
- LOGI(<span class="string">“the length of array is %d”</span>, len);
-
- <span class="comment">//如果长度为0, 返回arr </span>
- <span class="keyword">if</span>(len == 0)
- <span class="keyword">return</span> arr;
-
- <span class="comment">//如果长度大于0, 那么获取数组中的每个元素 </span>
- jint* p = (*env)->GetIntArrayElements(env, arr, 0);
-
- <span class="comment">//打印出数组中每个元素的值 </span>
- <span class="datatypes">int</span> i = 0;
- <span class="keyword">for</span>(; i < len; i ++)
- {
- LOGI(<span class="string">“arr[%d] = %d”</span>, i, *(p + i));
- }
-
- <span class="keyword">return</span> arr;
-
- }
</div>
</div>
<div>
**Java代码** :
</div>
<div>
<div class="dp-highlighter bg_java">
<div class="bar">
<div class="tools">
**[java]** [view plain](http://blog.csdn.net/shulianghan/article/details/18964835#)[copy](http://blog.csdn.net/shulianghan/article/details/18964835#)[](https://code.csdn.net/snippets/181707)[](https://code.csdn.net/snippets/181707/fork)
<div>
<embed id="ZeroClipboardMovie_41" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_41">
</embed>
</div>
</div>
</div>
- <span class="keyword">case</span> R.id.intMethod:
- <span class="keyword">int</span>[] array = {<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>};
- dataProvider.intMethod(array);
- <span class="keyword">break</span>;
</div>
**执行结果** : 上面的那种LogCat竟然启动失败, 只能将就着用这个了;</div>
<div>

</div>
<div>
</div>
## <a name="t49"></a>7. 本程序源码
<div>
</div>
<div>
**XML布局文件** :
</div>
<div>
<div class="dp-highlighter bg_html">
<div class="bar">
<div class="tools">
**[html]** [view plain](http://blog.csdn.net/shulianghan/article/details/18964835#)[copy](http://blog.csdn.net/shulianghan/article/details/18964835#)[](https://code.csdn.net/snippets/181707)[](https://code.csdn.net/snippets/181707/fork)
<div>
<embed id="ZeroClipboardMovie_42" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_42">
</embed>
</div>
</div>
</div>
- <span class="tag"><</span><span class="tag-name">LinearLayout</span> <span class="attribute">xmlns:android</span>=<span class="attribute-value">“http://schemas.android.com/apk/res/android”</span>
- <span class="attribute">xmlns:tools</span>=<span class="attribute-value">“http://schemas.android.com/tools”</span>
- <span class="attribute">android:orientation</span>=<span class="attribute-value">“vertical”</span>
- <span class="attribute">android:layout_width</span>=<span class="attribute-value">“match_parent”</span>
- <span class="attribute">android:layout_height</span>=<span class="attribute-value">“match_parent”</span> <span class="tag">></span>
-
- <span class="tag"><</span><span class="tag-name">Button</span>
- <span class="attribute">android:id</span>=<span class="attribute-value">“@+id/add”</span>
- <span class="attribute">android:layout_width</span>=<span class="attribute-value">“wrap_content”</span>
- <span class="attribute">android:layout_height</span>=<span class="attribute-value">“wrap_content”</span>
- <span class="attribute">android:text</span>=<span class="attribute-value">“调用 add 本地 方法”</span>
- <span class="attribute">android:onClick</span>=<span class="attribute-value">“onClick”</span><span class="tag">/></span>
-
- <span class="tag"><</span><span class="tag-name">Button</span>
- <span class="attribute">android:id</span>=<span class="attribute-value">“@+id/sayHelloInc”</span>
- <span class="attribute">android:layout_width</span>=<span class="attribute-value">“wrap_content”</span>
- <span class="attribute">android:layout_height</span>=<span class="attribute-value">“wrap_content”</span>
- <span class="attribute">android:text</span>=<span class="attribute-value">“调用 sayHelloInc 本地 方法”</span>
- <span class="attribute">android:onClick</span>=<span class="attribute-value">“onClick”</span><span class="tag">/></span>
-
- <span class="tag"><</span><span class="tag-name">Button</span>
- <span class="attribute">android:id</span>=<span class="attribute-value">“@+id/intMethod”</span>
- <span class="attribute">android:layout_width</span>=<span class="attribute-value">“wrap_content”</span>
- <span class="attribute">android:layout_height</span>=<span class="attribute-value">“wrap_content”</span>
- <span class="attribute">android:text</span>=<span class="attribute-value">“调用 intMethod 本地 方法”</span>
- <span class="attribute">android:onClick</span>=<span class="attribute-value">“onClick”</span><span class="tag">/></span>
-
- <span class="tag"></</span><span class="tag-name">LinearLayout</span><span class="tag">></span>
</div>
**Java源码** :</div>
<div>
— **MainActivity源码** :
</div>
<div>
<div class="dp-highlighter bg_java">
<div class="bar">
<div class="tools">
**[java]** [view plain](http://blog.csdn.net/shulianghan/article/details/18964835#)[copy](http://blog.csdn.net/shulianghan/article/details/18964835#)[](https://code.csdn.net/snippets/181707)[](https://code.csdn.net/snippets/181707/fork)
<div>
<embed id="ZeroClipboardMovie_43" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_43">
</embed>
</div>
</div>
</div>
- <span class="keyword">package</span> shuliang.han.ndkparameterpassing;
-
- <span class="keyword">import</span> android.app.Activity;
- <span class="keyword">import</span> android.os.Bundle;
- <span class="keyword">import</span> android.view.View;
- <span class="keyword">import</span> android.widget.Toast;
-
- <span class="keyword">public</span> <span class="keyword">class</span> MainActivity <span class="keyword">extends</span> Activity {
-
- <span class="keyword">static</span>{
- System.loadLibrary(<span class="string">“DataProvider”</span>);
- }
-
- DataProvider dataProvider;
- <span class="annotation">@Override</span>
- <span class="keyword">public</span> <span class="keyword">void</span> onCreate(Bundle savedInstanceState) {
- <span class="keyword">super</span>.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- dataProvider = <span class="keyword">new</span> DataProvider();
- }
-
- <span class="keyword">public</span> <span class="keyword">void</span> onClick(View view) {
-
- <span class="keyword">int</span> id = view.getId();
-
- <span class="keyword">switch</span> (id) {
- <span class="keyword">case</span> R.id.add:
- <span class="keyword">int</span> result = dataProvider.add(<span class="number">1</span>, <span class="number">2</span>);
- Toast.makeText(getApplicationContext(), <span class="string">“the add result : “</span> + result, Toast.LENGTH_LONG).show();
- <span class="keyword">break</span>;
-
- <span class="keyword">case</span> R.id.sayHelloInc:
- Toast.makeText(getApplicationContext(), dataProvider.sayHelloInc(<span class="string">“Hello”</span>), Toast.LENGTH_LONG).show();
- <span class="keyword">break</span>;
-
- <span class="keyword">case</span> R.id.intMethod:
- <span class="keyword">int</span>[] array = {<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>};
- dataProvider.intMethod(array);
- <span class="keyword">break</span>;
-
- <span class="keyword">default</span>:
- <span class="keyword">break</span>;
- }
- }
-
- }
</div>
—**DataProvider源码** :</div>
<div>
<div class="dp-highlighter bg_java">
<div class="bar">
<div class="tools">
**[java]** [view plain](http://blog.csdn.net/shulianghan/article/details/18964835#)[copy](http://blog.csdn.net/shulianghan/article/details/18964835#)[](https://code.csdn.net/snippets/181707)[](https://code.csdn.net/snippets/181707/fork)
<div>
<embed id="ZeroClipboardMovie_44" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_44">
</embed>
</div>
</div>
</div>
- <span class="keyword">package</span> shuliang.han.ndkparameterpassing;
-
- <span class="keyword">public</span> <span class="keyword">class</span> DataProvider {
-
- <span class="comment">//将Java中的两个int值 传给C语言, 进行相加后, 返回java语言 shuliang.han.ndkparameterpassing.DataProvider</span>
- <span class="keyword">public</span> <span class="keyword">native</span> <span class="keyword">int</span> add(<span class="keyword">int</span> x, <span class="keyword">int</span> y);
-
- <span class="comment">//将Java字符串传递给C语言, C语言处理字符串之后, 将处理结果返回给java</span>
- <span class="keyword">public</span> <span class="keyword">native</span> String sayHelloInc(String s);
-
- <span class="comment">//将java中的int数组传递给C语言, C语言为每个元素加10, 返回给Java</span>
- <span class="keyword">public</span> <span class="keyword">native</span> <span class="keyword">int</span>[] intMethod(<span class="keyword">int</span>[] nums);
-
- }
</div>
**JNI相关源码** :</div>
<div>
— **Android.mk源码** :
</div>
<div>
<div class="dp-highlighter bg_plain">
<div class="bar">
<div class="tools">
**[plain]** [view plain](http://blog.csdn.net/shulianghan/article/details/18964835#)[copy](http://blog.csdn.net/shulianghan/article/details/18964835#)[](https://code.csdn.net/snippets/181707)[](https://code.csdn.net/snippets/181707/fork)
<div>
<embed id="ZeroClipboardMovie_45" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_45">
</embed>
</div>
</div>
</div>
- LOCAL_PATH := $(call my-dir)
-
- include $(CLEAR_VARS)
-
- LOCAL_MODULE := DataProvider
- LOCAL_SRC_FILES := DataProvider.c
- #增加log函数对应的log库
- LOCAL_LDLIBS += -llog
-
- include $(BUILD_SHARED_LIBRARY)
</div>
—**DataProvider.c 主程序源码** :</div>
<div>
<div class="dp-highlighter bg_cpp">
<div class="bar">
<div class="tools">
**[cpp]** [view plain](http://blog.csdn.net/shulianghan/article/details/18964835#)[copy](http://blog.csdn.net/shulianghan/article/details/18964835#)[](https://code.csdn.net/snippets/181707)[](https://code.csdn.net/snippets/181707/fork)
<div>
<embed id="ZeroClipboardMovie_46" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_46">
</embed>
</div>
</div>
</div>
- <span class="preprocessor">#include <jni.h></span>
- <span class="preprocessor">#include <string.h></span>
- <span class="preprocessor">#include <android/log.h></span>
- <span class="preprocessor">#define LOG_TAG “System.out”</span>
- <span class="preprocessor">#define LOGD(…) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)</span>
- <span class="preprocessor">#define LOGI(…) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)</span>
-
- <span class="comment">// java中的jstring, 转化为c的一个字符数组</span>
- <span class="datatypes">char</span>* Jstring2CStr(JNIEnv* env, jstring jstr) {
- <span style=<span class="string">“white-space:pre”</span>> </span><span class="comment">//声明了一个字符串变量 rtn</span>
- <span style=<span class="string">“white-space:pre”</span>> </span><span class="datatypes">char</span>* rtn = NULL;
- <span style=<span class="string">“white-space:pre”</span>> </span><span class="comment">//找到Java中的String的Class对象</span>
- <span style=<span class="string">“white-space:pre”</span>> </span>jclass clsstring = (*env)->FindClass(env, <span class="string">“java/lang/String”</span>);
- <span style=<span class="string">“white-space:pre”</span>> </span><span class="comment">//创建一个Java中的字符串 “GB2312”</span>
- <span style=<span class="string">“white-space:pre”</span>> </span>jstring strencode = (*env)->NewStringUTF(env, <span class="string">“GB2312”</span>);
- <span style=<span class="string">“white-space:pre”</span>> </span><span class="comment">/*</span>
- <span class="comment"><span style=”white-space:pre”> </span> * 获取String中定义的方法 getBytes(), 该方法的参数是 String类型的, 返回值是 byte[]数组</span>
- <span class="comment"><span style=”white-space:pre”> </span> * “(Ljava/lang/String;)[B” 方法前面解析 :</span>
- <span class="comment"><span style=”white-space:pre”> </span> * — Ljava/lang/String; 表示参数是String字符串</span>
- <span class="comment"><span style=”white-space:pre”> </span> * — [B : 中括号表示这是一个数组, B代表byte类型, 返回值是一个byte数组</span>
- <span class="comment"><span style=”white-space:pre”> </span> */</span>
- <span style=<span class="string">“white-space:pre”</span>> </span>jmethodID mid = (*env)->GetMethodID(env, clsstring, <span class="string">“getBytes”</span>,
- <span style=<span class="string">“white-space:pre”</span>> </span><span class="string">“(Ljava/lang/String;)[B”</span>);
- <span style=<span class="string">“white-space:pre”</span>> </span><span class="comment">//调用Java中的getBytes方法, 传入参数介绍 参数②表示调用该方法的对象, 参数③表示方法id , 参数④表示方法参数</span>
- <span style=<span class="string">“white-space:pre”</span>> </span>jbyteArray barr = (jbyteArray)(*env)->CallObjectMethod(env, jstr, mid,
- <span style=<span class="string">“white-space:pre”</span>> </span>strencode); <span class="comment">// String .getByte(“GB2312”);</span>
- <span style=<span class="string">“white-space:pre”</span>> </span><span class="comment">//获取数组的长度</span>
- <span style=<span class="string">“white-space:pre”</span>> </span>jsize alen = (*env)->GetArrayLength(env, barr);
- <span style=<span class="string">“white-space:pre”</span>> </span><span class="comment">//获取数组中的所有的元素 , 存放在 jbyte*数组中</span>
- <span style=<span class="string">“white-space:pre”</span>> </span>jbyte* ba = (*env)->GetByteArrayElements(env, barr, JNI_FALSE);
- <span style=<span class="string">“white-space:pre”</span>> </span><span class="comment">//将Java数组中所有元素拷贝到C的char*数组中, 注意C语言数组结尾要加一个 ‘\0’</span>
- <span style=<span class="string">“white-space:pre”</span>> </span><span class="keyword">if</span> (alen > 0) {
- <span style=<span class="string">“white-space:pre”</span>> </span>rtn = (<span class="datatypes">char</span>*) malloc(alen + 1); <span class="comment">//new char[alen+1]; “\0”</span>
- <span style=<span class="string">“white-space:pre”</span>> </span>memcpy(rtn, ba, alen);
- <span style=<span class="string">“white-space:pre”</span>> </span>rtn[alen] = 0;
- <span style=<span class="string">“white-space:pre”</span>> </span>}
- <span style=<span class="string">“white-space:pre”</span>> </span>(*env)->ReleaseByteArrayElements(env, barr, ba, 0); <span class="comment">//释放内存</span>
-
-
- <span style=<span class="string">“white-space:pre”</span>> </span><span class="keyword">return</span> rtn;
- }
-
- <span class="comment">//方法签名, Java环境 和 调用native方法的类 必不可少, 后面的参数就是native方法的参数</span>
- jint Java_shuliang_han_ndkparameterpassing_DataProvider_add(JNIEnv * env, jobject obj, jint x, jint y)
- {
- <span class="comment">//Java中的int对应的是C语言中的long类型, 对应JNI中的jint类型, C语言中</span>
- LOGI(<span class="string">“JNI_log : x = %d , y = %d”</span> , x , y);
- <span class="keyword">return</span> x + y;
- }
-
- jstring Java_shuliang_han_ndkparameterpassing_DataProvider_sayHelloInc(JNIEnv *env, jobject obj, jstring str)
- {
- <span class="datatypes">char</span> *p = (<span class="datatypes">char</span>*)Jstring2CStr(env, str);
- <span class="comment">//打印Java传递过来的数据</span>
- LOGI(<span class="string">“Java JNI string parameter is : %s”</span>, p);
-
- <span class="datatypes">char</span> *append = <span class="string">“append”</span>;
-
- <span class="comment">//strcat(dest, source) 函数可以将source字符串 添加到dest字符串后面</span>
- <span class="keyword">return</span> (*env)->NewStringUTF(env, strcat(p, append));
- }
-
- jintArray Java_shuliang_han_ndkparameterpassing_DataProvider_intMethod(JNIEnv *env, jobject obj, jintArray arr)
- {
- <span class="comment">//获取arr大小</span>
- <span class="datatypes">int</span> len = (*env)->GetArrayLength(env, arr);
-
- <span class="comment">//在LogCat中打印出arr的大小</span>
- LOGI(<span class="string">“the length of array is %d”</span>, len);
-
- <span class="comment">//如果长度为0, 返回arr</span>
- <span class="keyword">if</span>(len == 0)
- <span class="keyword">return</span> arr;
-
- <span class="comment">//如果长度大于0, 那么获取数组中的每个元素</span>
- jint* p = (*env)->GetIntArrayElements(env, arr, 0);
-
- <span class="comment">//打印出数组中每个元素的值</span>
- <span class="datatypes">int</span> i = 0;
- <span class="keyword">for</span>(; i < len; i ++)
- {
- LOGI(<span class="string">“arr[%d] = %d”</span>, i, *(p + i));
- }
-
- <span class="keyword">return</span> arr;
-
- }
</div>
</div>
<div>
.
</div>
<div>
</div>
<div>
</div>
<div>
</div>
<div>
## <a name="t50"></a>8. 上传代码到GitHub
**创建新项目** : han1202012/NDKParameterPassing ;
— SSH地址 : git@github.com:han1202012/NDKParameterPassing.git ; — HTTP地址 : https://github.com/han1202012/NDKParameterPassing.git ;
💬 评论