发现最近记忆力非常差,估计和压力大有关系。今天在阅读《Android C++高级编程》这本书的时候,写了一个小的demo来利用SWIG工具来自动生成必要的JNI封装代码来简化
android c/c++项目的开发过程的 。在这里做个总结吧,和大家分享,也便于我今后忘记时快速回忆。毕竟好记性不如烂笔头!
1.什么是SWIG(Simplified Wrapper and Interface Generator)?
SWIG是一个编译时软件开发工具,它能生成将用C/C++编写的原生模块与包括Java在内(这里我们实际上只是验证了和java)编程语言进行关联的必要代码。
SWIG不仅是一个代码生成器,还是一个接口编译器。
SWIG把接口文件看做输入,并生成必要的代码,在Java中展示接口,从而让Java能够理解原生代码中接口的定义。
对于上面关于SWIG工具的介绍如果你感觉到生涩难懂,别着急,继续往下看,通过实例我想你会明白上面提到的“接口”,“C/C++封装”这些名词的含义的。
2.安装
ubuntu下安装很简单,因为我这里就是在ubuntu下安装的,因此就只介绍这一种环境下的安装:
1. sudo apt-get install swig
(哈哈,ubuntu下很简单吧!别的环境下,自行Google吧!)
3.试用SWIG
这一部分将通过一个具体的实例来介绍和试用SWIG。
我们要做一下几件事情:
写一个SWIG接口文件以向Java展示getuid函数;
将SWIG集成到Android构建过程中去;
将SWIG生成的源文件加入到Android.mk构建文件中;
用SWIG生成的代理类查询getuid函数;
在通过模拟器显示结果。
4.什么是接口文件?
SWIG接口文件包含函数原型、类、和变量的声明,它的语法和普通的C/C++头文件的语法是一样的。除了C/C++关键字和预处理指令,接口文件还包含SWIG特有的预处理指令。这些指令可用来优化生成封装代码。
步骤1:在工程的jni目录下新建一个文件,命名为Unix.i 。Unix.i就是接口文件,下面是Unix.i中的内容:
1. /*模块名*/
2. %module Unix
3. /*包含POSIX操作系统API*/
4. %{
5. #include<unistd.h>
6. %}
7. /*SWIG插入预处理指令的语法:
8. %< section >{
9. ...
10. 这个代码块将按照section的指定包含在生成代码的相应部分。
11. ...
12. %}
13. */
14. typedef unsigned int uid_t;
15.
16. /*让SWIG包装getuid函数*/
17. extern uid_t getuid(void);
这里要说明的一点是,SWIG能理解C/C++的所有类型,但是会把其他的东西全部看成对象并把它们封装成指针。
uid_t并不是一个标准的C/C++类型,因此如果直接使用的话SWIG会将其当作对象封装成一个指针,显然不合适。
因此理由C/C++类型定义定义一下就可以了。
而最后一句,extern uid_t getuid(void);让SWIG来封装getuid函数,从而在java中展示。
5.命令行下调用SWIG
步骤1:创建Java代理包:在调用SWIG前先创建java代理类包目录,即在src下创建一个com.example.swig包。
步骤2:调用SWIG:在NDK工程目录下执行命令行:
1. swig -java -package com.example.swig -outdir src/com/example/swig jni/Unix.i
执行完之后,你就可以在jni目录下看到Unix_wrap.c,同时在com.example.swig包内生成Java代理类UnixJNI.java和Unix.java。
6.将SWIG集成到Android构建过程中
通过前面的步骤,我们能体会到,手动的使用SWIG非常繁琐,那么下面来看看如何将SWIG集成到Android构建过程中吧。
步骤1:创建my_swig_generate_mk文件,内容如下:
1. #
2. #@author andy
3. #
4. #检查变量MY_SWIG_PACKAGE是否已经定义
5. ifndef MY_SWIG_PACKAGE
6. $(error MY_SWIG_PACAGE is not defined.)
7. endif
8. #用斜杠替换java目录中的圆点
9. MY_SWIG_OUTDIR:=$(NDK_PROJECT_PATH)/src/$(subst .,/,$(MY_SWIG_PACKAGE))
10.
11. #设置SWIG模式
12. ifndef MY_SWIG_TYPE
13. MY_SWIG_TYPE:=c
14. endif
15. ifeq ($(MY_SWIG_TYPE),cxx)
16. MY_SWIG_MODE:=-c++
17. else
18. MY_SWIG_MODE:=
19. endif
20. #追加SWIG封装源文件
21. LOCAL_SRC_FILES+= $(foreach MY_SWIG_INTERFACE,\
22. $(MY_SWIG_INTERFACES),\
23. $(basename $(MY_SWIG_INTERFACE)))_wrap.$(MY_SWIG_TYPE)
24. #添加.cxx作为c++文件扩展名
25. LOCAL_CPP_EXTENISON+=.cxx
26. #生成SWIG封代码
27. %_wrap.$(MY_SWIG_TYPE):%.i
28. $(call host-mkdir,$(MY_SWIG_OUTDIR))
29. swig -java \
30. $(MY_SWIG_MODE) \
31. -package $(MY_SWIG_PACKAGE) \
32. -outdir $(MY_SWIG_OUTDIR) \
33. $<
上面是源代码直接可以复制来用,但是看不到字体颜色,再附一副图片吧。
步骤2:将SWIG集成到Android.mk
打开Android.mk在如下位置添加代码:
1. LOCAL_PATH := $(call my-dir)
2.
3. include $(CLEAR_VARS)
4.
5. LOCAL_MODULE := hello-jni
6. LOCAL_SRC_FILES := hello-jni.c
7. MY_SWIG_PACKAGE:=com.example.swig
8. MY_SWIG_INTERFACES:= Unix.i
9. MY_SWIG_TYPE:=c
10. include $(LOCAL_PATH)/my_swig_generate.mk
11.
12. include $(BUILD_SHARED_LIBRARY)
其中7-10行代码为添加代码。这一步操作要在共享库hello-jni.so生成之前。
步骤3:我们打开终端,在命令行下进入当前NDK工程目录,执行:
1. ndk-build
结果如下:
从终端输出我们能看到,生成的Unix_wrap.c(C封装代码,其中包含处理类型映射以及将选中的原生函数展示给Java的JNI封装函数)
如图:
7.修改并运行HelloJni工程
在Hellojni.Java文件中进行如下修改:
1. -- tv.setText(stringFromJNI());
2. ++ tv.setText("UID: "+Unix.getuid());
在模拟器上运行应用程序Hellojni,可以看到下面结果:
8.总结:
开始写的时候还觉得要总结点什么,现在觉得好像学习的不够深入,所以就不总结了,以免误导大家,过一段时间深入学习了会更新blog,到时候再总结吧。