A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

本帖最后由 王震阳老师 于 2015-4-21 11:09 编辑







1. NDK简介(★★

1.1 NDK产生的背景
 Android平台从诞生起,就已经支持C、C++开发。众所周知,Android的SDK基于Java实现,这意味着基于Android SDK进行开发的第三方应用都必须使用Java语言。但这并不等同于“第三方应用只能使用Java”。在Android SDK首次发布时,Google就宣称其虚拟机Dalvik支持JNI编程方式,也就是第三方应用完全可以通过JNI调用自己的C动态库,即在Android平台上,“Java+C”的编程方式是一直都可以实现的。
  不过,Google也表示,使用原生SDK编程相比Dalvik虚拟机也有一些劣势,Android SDK文档里,找不到任何JNI方面的帮助。即使第三方应用开发者使用JNI完成了自己的C动态链接库(so)开发,但是so如何和应用程序一起打包成apk并发布?这里面也存在技术障碍。比如程序更加复杂,兼容性难以保障,无法访问Framework API,Debug难度更大等。开发者需要自行斟酌使用。
  于是NDK就应运而生了。NDK全称是Native Development Kit。
  NDK的发布,使“Java+C”的开发方式终于转正,成为官方支持的开发方式。NDK将是Android平台支持C开发的开端。
1.2 为什么使用NDK
1.代码的保护。由于apk的java层代码很容易被反编译,而C/C++库反编译难度较大。
   2.可以方便地使用现存的开源库。大部分现存的开源库都是用C/C++代码编写的。
   3.提高程序的执行效率。将要求高性能的应用逻辑使用C开发,从而提高应用程序的执行效率。
   4.便于移植。用C/C++写得库可以方便在其他的嵌入式平台上再次使用。
1.3 NDK简介
1.NDK是一系列工具的集合
NDK提供了一系列的工具,帮助开发者快速开发C(或C++)的动态库,并能自动将so和java应用一起打包成apk。NDK集成了交叉编译器,并提供了相应的mk文件隔离CPU、平台、ABI等差异,开发人员只需要简单修改mk文件(指出“哪些文件需要编译”、“编译特性要求”等),就可以创建出so。
2.NDK提供了一份稳定、功能有限的API头文件声明
Google明确声明该API是稳定的,在后续所有版本中都稳定支持当前发布的API。从该版本的NDK中看出,这些API支持的功能非常有限,包含有:C标准库(libc)、标准数学库(libm)、压缩库(libz)、Log库(liblog)。
1.4 NDK的安装
1.NDK的下载
NDK的官方下载地址http://developer.android.com/tools/sdk/ndk/index.html,由于官方网址在国外,国内访问不了,必须得翻墙。因此我提供了下载好的NDK工具放在百度网盘上供大家下载。http://pan.baidu.com/s/1jGpCDKi
2.将NDK解压到一个不包含空格和中文的目录下
本人将NDK解压在D:\software\ndkr9\android-ndk-r9b中。
1.5 NDK目录结构说明
build:该目录存放的使用NDK的mk脚本,mk脚本指定了编译参数
docs:该目录存放的是NDK的使用帮助文档
platforms:这里面存放的是与各个Android版本相关的平台(x86,arm,mips)相关C语言库和头文件
prebuilt:预编译工作目录
samples:存放的是演示程序
sources:存放的是NDK工具链的C语言源码
tests:测试相关的文件
toolchains:工具链,存放了三种架构的静态库等文件
ndk-build.cmd:Window平台使用NDK的命令
ndk-build:Linux平台使用NDK的命令
2. JNI入门(★★★
下面通过一个简单的JNI案例来演示如何使用JNI编程。
创建一个新的Android工程《JNI入门》,工程的最终目录结构如下图所示。
在MainActivity.java类中定义一个native方法
在工程跟目录下创建一个文件夹jni,该目录名称是约定(约定优于配置)好的,不能是其他名字。
在jni目录下创建hello.c源文件,文件名可以按照见名知意的规则来创建。hello.c代码清单如下。
:上面的代码虽然简单但是关于jni.h头文件和方法名必须单独说明。
1)jni.h头文件位于NDK安装目录下/platforms/android-*/(某平台)/usr/include目录中,如下图
上面的某平台指CPU的三种架构如下图。我们选择任意一架构皆可,但是对于手机来说CPU用arm架构的最多,x86次之,mips架构最少。
2)JNI中C源文件方法名的命名规则
这里的命名规则指用于跟java文件中native方法对应的C语言方法,而C语言中的其他方法命名只要符合C语言规则就行。jstring Java_com_itheima_jnihello_MainActivity_helloC(JNIEnv* env, jobject obj) 中,jstring是方法返回值类型,我们可以把jstring看成是java中String跟C语言中char*类型的一个中间转换类型,java跟C语言的数据类型是不一样的,他们之间要想互相调用就必须通过一种中介来实现,这个中介就是在jni.h头文件中定义的。关于更多的转换类型,在本文档的第2章会有更详细的说明。
方法名第一个字母必须是Java,首单词大写,然后下划线_,然后是将该方法所在的包、类、方法用“_”连接起来,比如com.itheima.jnihello.MainActivity类中的helloC方法,转变成C语言中的方法名为Java_com_itheima_jnihello_MainActivity_helloC。
方法的形参有两个是必须的也就是不管java中的方法是否有形参,但是C语言中对应的方法必须有JNIEnv* env,和jobject obj,如果java方法中还用其他形参,那么在C语言中严格按照顺序排在jobject obj参数的后面即可。
上面的env代表指向JVM的指针,obj是调用该方法的java对象。
使用NDK工具将hello.c编译成hello.so文件
为了方便直接在控制台中使用NDK工具的ndk-build.cmd命令,我们首先将ndk-build.cmd所在的目录设置成系统环境变量。环境变量配置好以后,在命令行中输入ndk-build.cmd会有如下提示:
将当前目录切换到hello.c所在的工程目录
这时候如果直接输入ndk-build.cmd那么会出现如下异常:
出现这种错误时因为我们并没有告诉ndk我们要将那个C语言源代码编译成目标文件。为了告诉ndk要将那个C源文件编译成目标文件,我们需要在工程中的jni目录中添加Android.mk配置文件。
在当前工程的jni目录下添加Android.mk配置文件,该配置文件可以从ndk安装目录的实例代码中拷贝,然后修改。
Android.mk文件清单如下,我们只需要修改LOCAL_MODULE和LOCAL_SRC_FILES两个参数即可。LOCAL_MODULE参数是指定编译后的目标文件的名称,其实编译好的目标文件名为libhello.so,LOCAL_SRC_FILES指定了要编译的源文件。
在cmd中,将当前目录切换到hello.c所在目录,然后重新执行ndk-build.cmd命令,这次成功编译,cmd显示效果如下图所示。
查看项目目录结构,发现在libs目录中多了两个文件夹armeabi和x86,这两个文件夹下分别包含了一个libhello.so动态链接库。这也代表着当前工程中的动态库支持arm架构和x86架构的cpu。
:可能你的并没有同时生成这两个文件,是因为我的工程中引入了Application.mk文件,因此你需要引入该文件。
Application.mk文件清单:
该清单其实只有一行内容,第一行是注释。APP_ABI参数指定要生成的目标文件支持的平台都有哪些,默认是armeabi如果想支持多个平台只需要空一格然后写出其他平台名字即可。
在MainActivity.java中调用C语言
运行上面工程,效果如下:
:如果我们编译的arm平台的so文件,但是却部署到了x86平台的模拟器上,那么运行的时候会报找不到libhello.so的异常。
3. JNI规范(3.1 JNI数据类型和数据结构
1)基本数据类型
JNI基本类型和本地等效类型的对应表格如下:
Java 类型
本地 C 类型
实际表示的 C 类型
Win32
说明
boolean
jboolean
unsigned char
无符号,8
byte
jbyte
signed char
有符号,8
char
jchar
unsigned short
无符号,16
short
jshort
short
有符号,16
int
jint
long
有符号,32
long
jlong
__int64
有符号,64
float
jfloat
float
32
double
jdouble
double
64
void
void
N/A
N/A
2)引用类型,JNI还包含了很对对应于不同Java对象的引用类型,JNI引用类型的组织层次如下图所示:
3.2 JNI接口函数命名方式
1)类型签名
Java虚拟机的类型签名如下:
类型签名
Java类型
Z
boolean
B
byte
C
char
S
short
I
int
J
long
F
float
D
double
Lfully-qulitied-class;
全限定类
[type
type[] 数组
(argtypes)rettype
方法类型
例如,Java方法int feet(int n, String s,int [] arr)的类型签名如下:
(ILJava/lang/String;[I)I
圆括号里面为参数,I表示第一个参数int型,LJava/lang/String;表示第二个参数为全限定Java.lang.String类型,[I表示第三个参数为int型的数组,圆括号后面为返回值类型,I表示返回值为int型。
2)一般函数的JNI接口函数命名方式
一般JNI接口函数命名如下:
Java_包名_类名_方法名。
例如:某工程下com/itheima包下MainActivity类的int getIntFromC()方法的C语言实现函数命名如下:
jint Java_com_itheima_MainActivity_getIntFromC(JNIEnv* env,jobject obj)
其中,包名所包含的“/”应全部以下划线替代,其本地实现的参数和返回值也应转换为JNI类型。
3) 重载函数的JNI接口函数命名方式
重载函数的JNI实现在一般函数的JNI实现之外,还应添加上类型签名以作为同名函数之间的区别,其接口函数命名如下:Java_包名_类名_方法名_参数签名。
例如:某工程下com/itheima包下MainActivity类的int getIntFromC(int n, String s,int [] arr)方法的C语言实现函数命名如下:
jint Java_com_itheima_MainActivity_getIntFromC_ILJava_lang_String_2_3I
(JNIEnv* env, jobject obj, jint n, jstring s, jintarray arr)。
JNI在函数命名时采用名字扰乱方案,以保证所有的Unicode字符都能
转换为有效的C函数名,所有的“/”,无论是包名中的还是全限定类名中的,均使用“_”代替,用_0,„,_9来代替转义字符,如下:
转义字符序列
表示
_0XXXX
Unicode字符XXXX
_1
字符“_
_2
签名中的字符“;”
_3
签名中的字符“[
3.3 JNI函数与API
在本文档中我们所主要需要关心的是C/C++数据类型与JNI本地类型之间的转化过程,这个过程某些数据的转换需要使用JNIEnv对象的一系列方法来完成。
1)jstring转换为C风格字符串
char* test = (char*)(*env)->GetStringUTFChars(env,jstring,NULL);
使用完毕后,应调用:
(*env)->ReleaseStringUTFChars(env, jstring, test);
释放资源。
2)C风格字符串转换为jstring
char charStr[50];
jstring jstr;
jstr = env -> NewStringUTF(charStr);
3)C语言中获取的一段char*的buffer传递给Java
在jni中new一个byte数组,然后使用
(*env)->SetByteArrayRegion(env, bytearray, 0, len, buffer) 操作将buffer拷贝到数组中。
这种方式主要是针对buffer中存在“\0”的情况,如果以C风格字符串的方式读入,就会损失“\0”之后的字符。
4)数组操作
JNI函数
功能
GetArrayLength
返回数组中的元素数
NewObjectArray
创建一个指定长度的原始数据类型数组
GetObjectArrayElement
返回Object数组的元素
SetObjectArrayElement
设置Object数组的元素
GetObjectArrayRegion
将原始数据类型数组中的内容拷贝到预先分配好的内存缓存中
SetObjectArrayRegion
设置缓存中数组的值
ReleaseObjectArrayRegion
释放GetObjectArrayRegion分配的内存
:对int,char等基本数据类型的数组操作,将相关Object名称替换为对应基本数据类型名称即为相关函数。
数组操作的方法选择基于使用者的需求而定,如果使用者需要在内存中拷贝数组并对其进行操作那么一般使用GetObjectArrayRegion和SetObjectArrayRegion函数,否则一般使用SetObjectArrayElement和GetObjectArrayElement函数。


4. 案例-银行登录系统(★★
需求:假设银行的登陆模块是用C语言来编写的,但是我们的Android应用想登陆银行系统,那么就需要通过JNI来实现了。
创建一个新Android工程《建行客户端》,工程目录结构如下图。
在工程中创建jni文件夹,然后将jni.h、Android.mk、Application.mk从JNI入门工程拷贝进去。
在jni目录下创建login.c文件,在该文件中实现登录业务逻辑。代码清单如下。
是用ndk工具,将login.c编译成动态库文件。编译前修改Android.mk文件的LOCAL_SRC_FILES := login.c
编写在MainActivity.java类
布局文件比较简单,这里就不再给出。
运行上面的代码,运行结果如下:

5. CDT插件的安装(5.1 CDT简介
CDT 项目致力于为 Eclipse 平台提供功能完全的 C/C++ 集成开发环境(Integrated Development Environment,IDE)。CDT 是完全用 Java 实现的开放源码项目(根据 Common Public License 特许的),它作为 Eclipse SDK 平台的一组插件。这些插件将 C/C++ 透视图添加到 Eclipse 工作台(Workbench)中,现在后者可以用许多视图和向导以及高级编辑和调试支持来支持 C/C++ 开发。
5.2 CDT的下载
CDT插件可以通过eclipse的在线安装,但是受限于跨国家网络访问,一般不是很好用。因此这里我主要给大家说的是如何离线安装。
下载CDT离线安装包。针对不同版本eclipse的cdt安装包如下,大家可以从我的百度网盘上直接下载。考虑到我们大部分都用的最新的ADT因此建议选择8.5.0版本的CDT。
cdt-8.5.0-for-Eclipse-Luna [color=rgb(87, 137, 220) !important]http://pan.baidu.com/s/1c0m1k0w
cdt-8.3.0-for-Eclipse-Kepler [color=rgb(87, 137, 220) !important]http://pan.baidu.com/s/1kT21QOf
cdt-8.1.2-for-Eclipse-Juno [color=rgb(87, 137, 220) !important]http://pan.baidu.com/s/1qWAzjBI
选择eclipse的Help->Install New Software...,弹出如下对话框
点击Add按钮,在弹出的对话框中输入Name。在Location栏如果输入一个http地址是让eclipse自动从网络上下载安装,这里我们点击Archive按钮找到我们事先下载好的离线安装包。然后点击OK。
将CDT所有的插件勾选上,同时将最下面的联网检查更新去掉勾选,然后点击Next,直到Finish。
安装好以后在File->New->Other中会有C/C++选项,如下图。
在Open Perspective中也多了C/C++视图可选项,如下图。
安装好以后,我们就可以在eclipse中开发我们的C/C++工程了。不过对我们Android开发人员来说用到的机会不是很多。就算是开发C/C++工程,大多数程序员也不会选择在eclipse平台上进行开发。Eclipse更多的是专注于Java语言项目的开发,比如JavaEE、Android。















至此,本文档完!

2015年1月28日 星期三 21:37:10
北京市海淀区东北旺中路东馨园小区


126 个回复

正序浏览
不错,赞一个
回复 使用道具 举报
好贴好贴
回复 使用道具 举报
好东西,先收藏了
回复 使用道具 举报
北京校区学员发来贺电
回复 使用道具 举报
谢谢阳哥分享
回复 使用道具 举报
阳哥威武
回复 使用道具 举报
满满的干货,阳哥赞一个
回复 使用道具 举报
回复 使用道具 举报
谢谢总结的这么详细
回复 使用道具 举报
只能赞了,经典不用说了。
回复 使用道具 举报
多谢指点啊
回复 使用道具 举报
好好学习.
回复 使用道具 举报
内容好多 慢慢看  谢谢
回复 使用道具 举报
支持一下,赞一个。
回复 使用道具 举报
感觉很好
回复 使用道具 举报
感谢阳哥!
回复 使用道具 举报
guoyangpeng 来自手机 中级黑马 2015-5-17 08:31:49
109#
好东西,等学完Javase学
回复 使用道具 举报
大菠萝 发表于 2015-5-13 03:01
阳哥看起来好牛逼的样子,只不过现在不是主推android studio吗?怎么还用eclipse? ...

那现在上课还是用的eclipse吗?
回复 使用道具 举报
mark,,,,,,,,,,,,,,,,,,,,,,,
回复 使用道具 举报
                      果断懂不了、
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马