发现最近记忆力非常差,估计和压力大有关系。今天在阅读《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下安装的,因此就只介绍这一种环境下的安装:
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中的内容:
/*模块名*/
%module Unix
/*包含POSIX操作系统API*/
%{
#include
%}
/*SWIG插入预处理指令的语法:
%< section >{
...
这个代码块将按照section的指定包含在生成代码的相应部分。
...
%}
*/
typedef unsigned int uid_t;
/*让SWIG包装getuid函数*/
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工程目录下执行命令行:
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文件,内容如下:
#
#@author andy
#
#检查变量MY_SWIG_PACKAGE是否已经定义
ifndef MY_SWIG_PACKAGE
$(error MY_SWIG_PACAGE is not defined.)
endif
#用斜杠替换java目录中的圆点
MY_SWIG_OUTDIR:=$(NDK_PROJECT_PATH)/src/$(subst .,/,$(MY_SWIG_PACKAGE))
#设置SWIG模式
ifndef MY_SWIG_TYPE
MY_SWIG_TYPE:=c
endif
ifeq ($(MY_SWIG_TYPE),cxx)
MY_SWIG_MODE:=-c++
else
MY_SWIG_MODE:=
endif
#追加SWIG封装源文件
LOCAL_SRC_FILES+= $(foreach MY_SWIG_INTERFACE,\
$(MY_SWIG_INTERFACES),\
$(basename $(MY_SWIG_INTERFACE)))_wrap.$(MY_SWIG_TYPE)
#添加.cxx作为c++文件扩展名
LOCAL_CPP_EXTENISON+=.cxx
#生成SWIG封代码
%_wrap.$(MY_SWIG_TYPE):%.i
$(call host-mkdir,$(MY_SWIG_OUTDIR))
swig -java \
$(MY_SWIG_MODE) \
-package $(MY_SWIG_PACKAGE) \
-outdir $(MY_SWIG_OUTDIR) \
$<
上面是源代码直接可以复制来用,但是看不到字体颜色,再附一副图片吧。
步骤2:将SWIG集成到Android.mk
打开Android.mk在如下位置添加代码:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := hello-jni
LOCAL_SRC_FILES := hello-jni.c
MY_SWIG_PACKAGE:=com.example.swig
MY_SWIG_INTERFACES:= Unix.i
MY_SWIG_TYPE:=c
include $(LOCAL_PATH)/my_swig_generate.mk
include $(BUILD_SHARED_LIBRARY)
其中7-10行代码为添加代码。这一步操作要在共享库hello-jni.so生成之前。
步骤3:我们打开终端,在命令行下进入当前NDK工程目录,执行:
ndk-build
结果如下:
从终端输出我们能看到,生成的Unix_wrap.c(C封装代码,其中包含处理类型映射以及将选中的原生函数展示给Java的JNI封装函数)
如图:
7.修改并运行HelloJni工程
在Hellojni.Java文件中进行如下修改:
-- tv.setText(stringFromJNI());
++ tv.setText("UID: "+Unix.getuid());
在模拟器上运行应用程序Hellojni,可以看到下面结果:
8.总结:
开始写的时候还觉得要总结点什么,现在觉得好像学习的不够深入,所以就不总结了,以免误导大家,过一段时间深入学习了会更新blog,到时候再总结吧。