Android NDK项目中使用SWIG生成JAVA类和C/C++封装代码

来源:互联网 发布:java测试 博客园 编辑:程序博客网 时间:2024/06/14 03:33

发现最近记忆力非常差,估计和压力大有关系。今天在阅读《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<unistd.h> %}/*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_TYPEMY_SWIG_TYPE:=cendififeq ($(MY_SWIG_TYPE),cxx)MY_SWIG_MODE:=-c++elseMY_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-jniLOCAL_SRC_FILES := hello-jni.c MY_SWIG_PACKAGE:=com.example.swigMY_SWIG_INTERFACES:= Unix.iMY_SWIG_TYPE:=cinclude $(LOCAL_PATH)/my_swig_generate.mkinclude $(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,到时候再总结吧。



0 0