发现最近记忆力非常差,估计和压力大有关系。今天在阅读《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包。


android 打包封装aar android代码封装_java

步骤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。


android 打包封装aar android代码封装_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.     $<


上面是源代码直接可以复制来用,但是看不到字体颜色,再附一副图片吧。


android 打包封装aar android代码封装_java_03

步骤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


结果如下:


android 打包封装aar android代码封装_android 打包封装aar_04

从终端输出我们能看到,生成的Unix_wrap.c(C封装代码,其中包含处理类型映射以及将选中的原生函数展示给Java的JNI封装函数)

如图:


android 打包封装aar android代码封装_java_05

7.修改并运行HelloJni工程

在Hellojni.Java文件中进行如下修改:


    1. -- tv.setText(stringFromJNI());  
    2. ++ tv.setText("UID: "+Unix.getuid());


    在模拟器上运行应用程序Hellojni,可以看到下面结果:


    android 打包封装aar android代码封装_java_06

    8.总结:

    开始写的时候还觉得要总结点什么,现在觉得好像学习的不够深入,所以就不总结了,以免误导大家,过一段时间深入学习了会更新blog,到时候再总结吧。