Swig java Jni開發(fā)指南
目錄
Swig java Jni開發(fā)指南
- Swig參考文檔
- 全局變量
- 常量
- 只讀變量
- 枚舉
- 函數(shù)
- 結(jié)構(gòu)體
- 類
- 調(diào)用函數(shù)指針
- 回調(diào)
- 類型轉(zhuǎn)換
- 兩個常用命令
簡介:
JNI:Java Native Interface,它允許Java代碼和其他語言(尤其C/C++)寫的代碼進行交互,只要遵守調(diào)用約定即可。
JNA:Java Native Access是一個開源的Java框架,是Sun公司推出的一種調(diào)用本地方法的技術(shù),是建立在經(jīng)典的JNI基礎(chǔ)之上的一個框架。之所以說它是JNI的替 代者,是因為JNA大大簡化了調(diào)用本地方法的過程,使用很方便,基本上不需要脫離Java環(huán)境就可以完成。
Swig可以根據(jù)c或c++代碼生成jni代碼的工具,大大簡化jni的開發(fā)
Jnaerator可以根據(jù)c或c++代碼生成jna代碼的工具,大大簡化jna的開發(fā)
從難易度看,使用jnaerator開發(fā)jna最簡單,代碼基本都是自動生成,但是jna開發(fā)有個很大的缺點,就是如果c代碼過于復(fù)雜,比如出現(xiàn)java調(diào)用c,然后c再回調(diào)java,java返回的結(jié)果c還需要繼續(xù)處理的時候,經(jīng)常出現(xiàn)不可控制的crash,而jna算是中間層,這個層出現(xiàn)的錯誤完全無法調(diào)試,被逼無奈,我們的項目先用jna開發(fā),不得不轉(zhuǎn)jni開發(fā),在使用swig的過程中,也遇到不少問題,因此總結(jié)如下:
- Swig參考文檔
http://www.swig.org/Doc3.0/SWIGDocumentation.html
swig是一個編譯時軟件開發(fā)工具,它能生成將用c/c++編寫的原生模塊與包括java在內(nèi)的其他編程語言進行鏈接的必要代碼。Swig不僅是一個代碼生成器,還是一個接口編譯器。它不定義新的協(xié)議,也不是一個組件框架或者一個特定的運行時庫。Swig把接口文件看做輸入,并生成必要的代碼在java中展示接口,從而讓java能夠理解原生代碼中的接口定義。Swig不是一個存根生成器;它產(chǎn)生將要被編譯和運行的代碼。
Swig可應(yīng)用于包括windows、mac os x和linux在內(nèi)的大多數(shù)操作系統(tǒng)平臺。大家可以參考官網(wǎng)文檔安裝。
使用swig需要生成一個.i的接口文件,swig接口文件包含函數(shù)原型、類和變量聲明,它的語法和普通的c/c++頭文件一樣。除了c/c++關(guān)鍵字和預(yù)處理器指令,接口文件還包含swig特有的預(yù)處理器指令,該指令可用于優(yōu)化生成封裝代碼。 - 全局變量
a) 編寫example.h
int counter = 0;
b) 編寫example.i
%module example
%{
#include "example.h"
%}
extern int counter;
c) 編寫runme.java
/ runme.java
ublic class runme {
static {
System.loadLibrary("example");
}
public static void main(String argv[]) {
System.out.println(example.getCounter());
example.setCounter(1);
System.out.println(example.getCounter());
}
d) 執(zhí)行以下命令
swig -java example.i
gcc -Wall -Wl,--kill-at -I "%JAVA_HOME%/include" -I "%JAVA_HOME%/include/win32" -shared -s -o example.dll *.c
javac -d . *.java
java -cp . runme
- 常量
a) 編寫example.i
%module example
/* 用define指令定義常量 */
#define MAX_WIDTH 640
/* 用%constant指令定義常量 */
%constant int MAX_HEIGHT = 320;
b) 編寫runme.java
// runme.java
public class runme {
static {
System.loadLibrary("example");
}
public static void main(String argv[]) {
System.out.println(example.MAX_WIDTH);
System.out.println(example.MAX_HEIGHT);
}
}
c) 執(zhí)行以下命令,同1.d
- 只讀變量
區(qū)別只是在生成的包裝類中,只讀的只有g(shù)et函數(shù),讀寫的有g(shù)et和set函數(shù)
a) 編寫example.h
/* 只讀變量 */
int readOnly;
/* 讀-寫變量 */
int readWrite;
b) 編寫example.i
%module example
%{
#include "example.h"
%}
/* 只讀變量 */
%immutable readOnly;
int readOnly;
/* 讀-寫變量 */
int readWrite;
c) 編寫runme.java
// runme.java
public class runme {
static {
System.loadLibrary("example");
}
public static void main(String argv[]) {
System.out.println(example.getReadOnly());
example.setReadWrite(10);
System.out.println(example.getReadWrite());
}
}
d) 執(zhí)行以下命令,同1.d
- 枚舉
a) 編寫example.h
/* 匿名枚舉 */
enum {ONE = 1, TWO = 2, THREE, FOUR};
/* 命名枚舉 */
enum Color { RED = 1, GREEN, BLANK, YELLOW};
enum SeasonEnum{ SPRING = 1,SUMMER,AUTUMN,WINTER};
b) 編寫example.i
%module example
%{
#include "example.h"
%}
/* 匿名枚舉 */
enum {ONE = 1, TWO = 2, THREE, FOUR};
/* 命名枚舉 */
enum Color { RED = 1, GREEN, BLANK, YELLOW};
/* 類型不安全,這個用起來最簡單 */
%include "enumtypeunsafe.swg"
%javaconst(1);
enum SeasonEnum{ SPRING = 1,SUMMER,AUTUMN,WINTER};
c) 編寫runme.java
// runme.java
public class runme {
static {
System.loadLibrary("example");
}
public static void main(String argv[]) {
System.out.println(example.ONE + "," + example.TWO + "," + example.THREE + "," + example.FOUR);
System.out.println(Color.RED.swigValue() + "," + Color.GREEN.swigValue() + "," + Color.BLANK.swigValue() + "," + Color.YELLOW.swigValue());
System.out.println(SeasonEnum.SPRING + "," + SeasonEnum.SUMMER + "," + SeasonEnum.AUTUMN + "," + SeasonEnum.WINTER);
}
}
d) 執(zhí)行以下命令,同1.d
- 函數(shù)
這里直接抄swigwin-3.0.12\Examples\java\simple
a) 編寫example.c
/* File : example.c */
/* A global variable */
double Foo = 3.0;
/* Compute the greatest common divisor of positive integers */
int gcd(int x, int y) {
int g;
g = y;
while (x > 0) {
g = x;
x = y % x;
y = g;
}
return g;
}
b) 編寫example.i
/* File : example.i */
%module example
%inline %{
extern int gcd(int x, int y);
extern double Foo;
%}
c) 編寫runme.java
public class runme {
static {
try {
System.loadLibrary("example");
} catch (UnsatisfiedLinkError e) {
System.err.println("Native code library failed to load. See the chapter on Dynamic Linking Problems in the SWIG Java documentation for help.\n" + e);
System.exit(1);
}
}
public static void main(String argv[]) {
// Call our gcd() function
int x = 42;
int y = 105;
int g = example.gcd(x,y);
System.out.println("The gcd of " + x + " and " + y + " is " + g);
// Manipulate the Foo global variable
// Output its current value
System.out.println("Foo = " + example.getFoo());
// Change its value
example.setFoo(3.1415926);
// See if the change took effect
System.out.println("Foo = " + example.getFoo());
}
}
d) 執(zhí)行以下命令,同1.d
- 結(jié)構(gòu)體
a) 編寫example.h
// example.h
struct Books
{
char title[50];
char author[50];
char subject[100];
int book_id;
};
extern void show(struct Books* book);
b) 編寫example.c
// example.c
#include <stdio.h>
#include "example.h"
// struct Books book = {"C 語言", "RUNOOB", "編程語言", 123456};
void show(struct Books* book)
{
printf("title : %s\nauthor: %s\nsubject: %s\nbook_id: %d\n", book->title, book->author, book->subject, book->book_id);
}
c) 編寫example.i
/* File : example.i */
%module example
%{
#include "example.h"
%}
%include "example.h"
d) 編寫runme.java
// runme.java
public class runme {
static {
System.loadLibrary("example");
}
public static void main(String argv[]) {
Books book = new Books();
book.setTitle("C 語言");
book.setAuthor("RUNOOB");
book.setSubject("編程語言");
book.setBook_id(123456);
example.show(book);
}
}
e) 執(zhí)行以下命令,同1.d
- 類
這里直接抄swigwin-3.0.12\Examples\java\ class,對比java調(diào)用c的結(jié)構(gòu)體來看,兩個的處理完全一樣,都是把c的結(jié)構(gòu)或類包裝成java類使用
a) 編寫example.h
/* File : example.h */
class Shape {
public:
Shape() {
nshapes++;
}
virtual ~Shape() {
nshapes--;
}
double x, y;
void move(double dx, double dy);
virtual double area() = 0;
virtual double perimeter() = 0;
static int nshapes;
};
class Circle : public Shape {
private:
double radius;
public:
Circle(double r) : radius(r) { }
virtual double area();
virtual double perimeter();
};
class Square : public Shape {
private:
double width;
public:
Square(double w) : width(w) { }
virtual double area();
virtual double perimeter();
};
b) 編寫example.cxx
/* File : example.cxx */
#include "example.h"
#define M_PI 3.14159265358979323846
/* Move the shape to a new location */
void Shape::move(double dx, double dy) {
x += dx;
y += dy;
}
int Shape::nshapes = 0;
double Circle::area() {
return M_PI*radius*radius;
}
double Circle::perimeter() {
return 2*M_PI*radius;
}
double Square::area() {
return width*width;
}
double Square::perimeter() {
return 4*width;
}
c) 編寫example.i
/* File : example.i */
%module example
%{
#include "example.h"
%}
/* Let's just grab the original header file here */
%include "example.h"
d) 編寫runme.java
// This example illustrates how C++ classes can be used from Java using SWIG.
// The Java class gets mapped onto the C++ class and behaves as if it is a Java class.
public class runme {
static {
try {
System.loadLibrary("example");
} catch (UnsatisfiedLinkError e) {
System.err.println("Native code library failed to load. See the chapter on Dynamic Linking Problems in the SWIG Java documentation for help.\n" + e);
System.exit(1);
}
}
public static void main(String argv[])
{
// ----- Object creation -----
System.out.println( "Creating some objects:" );
Circle c = new Circle(10);
System.out.println( " Created circle " + c );
Square s = new Square(10);
System.out.println( " Created square " + s );
// ----- Access a static member -----
System.out.println( "\nA total of " + Shape.getNshapes() + " shapes were created" );
// ----- Member data access -----
// Notice how we can do this using functions specific to
// the 'Circle' class.
c.setX(20);
c.setY(30);
// Now use the same functions in the base class
Shape shape = s;
shape.setX(-10);
shape.setY(5);
System.out.println( "\nHere is their current position:" );
System.out.println( " Circle = (" + c.getX() + " " + c.getY() + ")" );
System.out.println( " Square = (" + s.getX() + " " + s.getY() + ")" );
// ----- Call some methods -----
System.out.println( "\nHere are some properties of the shapes:" );
Shape[] shapes = {c,s};
for (int i=0; i<shapes.length; i++)
{
System.out.println( " " + shapes[i].toString() );
System.out.println( " area = " + shapes[i].area() );
System.out.println( " perimeter = " + shapes[i].perimeter() );
}
// Notice how the area() and perimeter() functions really
// invoke the appropriate virtual method on each object.
// ----- Delete everything -----
System.out.println( "\nGuess I'll clean up now" );
// Note: this invokes the virtual destructor
// You could leave this to the garbage collector
c.delete();
s.delete();
System.out.println( Shape.getNshapes() + " shapes remain" );
System.out.println( "Goodbye" );
}
}
e) 執(zhí)行以下命令
swig -c++ -java example.i
g++ -Wall -Wl,--kill-at -I "%JAVA_HOME%/include" -I "%JAVA_HOME%/include/win32" -shared -s -o example.dll *.cxx
javac -d . *.java
java -cp . runme
- 調(diào)用函數(shù)指針
重點:這個只能獲得函數(shù)指針,然后多寫個函數(shù)把函數(shù)指針傳遞給c層來調(diào)用,java層沒有找到方法調(diào)用
這種方法會出現(xiàn)一些比較奇怪的類名,例如:SWIGTYPE_p_f_float_float__float,為了類名可讀,可以用下一部分說的回調(diào)來處理,只是不需要java繼承,直接get出來,然后調(diào)用run就可以用了
a) 編寫example.h
/****************************************
* 函數(shù)指針結(jié)構(gòu)體
***************************************/
typedef float (*callback_t)(float a, float b);
typedef struct _OP {
callback_t p_add;
callback_t p_sub;
callback_t p_mul;
callback_t p_div;
} OP;
extern OP* get_init_op(void);
extern float add_sub_mul_div(float a, float b, callback_t fun);
b) 編寫example.i
/* File : example.i */
%module example
%{
#include "example.h"
%}
%include "example.h"
c) 編寫example.c
#include <stdio.h>
#include <stdlib.h>
#include "example.h"
/****************************************
* 加減乘除函數(shù)
***************************************/
float ADD(float a, float b)
{
return a + b;
}
float SUB(float a, float b)
{
return a - b;
}
float MUL(float a, float b)
{
return a * b;
}
float DIV(float a, float b)
{
return a / b;
}
/****************************************
* 初始化函數(shù)指針
***************************************/
OP* get_init_op(void)
{
OP *op = (OP *)malloc(sizeof(OP));
op->p_add = ADD;
op->p_sub = SUB;
op->p_mul = MUL;
op->p_div = DIV;
return op;
}
/****************************************
* 庫函數(shù)
***************************************/
float add_sub_mul_div(float a, float b, callback_t fun)
{
return fun(a, b);
}
int main()
{
OP *op = get_init_op();
/* 直接使用函數(shù)指針調(diào)用函數(shù) */
printf("ADD = %f, SUB = %f, MUL = %f, DIV = %f\n", (op->p_add)(1.3, 2.2), (*op->p_sub)(1.3, 2.2),
(op->p_mul)(1.3, 2.2), (*op->p_div)(1.3, 2.2));
/* 調(diào)用回調(diào)函數(shù) */
printf("ADD = %f, SUB = %f, MUL = %f, DIV = %f\n",
add_sub_mul_div(1.3, 2.2, ADD),
add_sub_mul_div(1.3, 2.2, SUB),
add_sub_mul_div(1.3, 2.2, MUL),
add_sub_mul_div(1.3, 2.2, DIV));
return 0;
}
d) 編寫runme.java
// runme.java
public class runme {
static {
System.loadLibrary("example");
}
public static void main(String argv[]) {
OP op = example.get_init_op();
System.out.println(example.add_sub_mul_div(1.3f, 2.2f,op.getP_add()));
System.out.println(example.add_sub_mul_div(1.3f, 2.2f,op.getP_sub()));
System.out.println(example.add_sub_mul_div(1.3f, 2.2f,op.getP_mul()));
System.out.println(example.add_sub_mul_div(1.3f, 2.2f,op.getP_div()));
}
}
e) 執(zhí)行以下命令,同1.d
- 回調(diào)
這個c代碼搞不定,只能通過c++類實現(xiàn),參考上例修改實現(xiàn)
參考:swigwin-3.0.12\Examples\java\callback
a) 編寫example.h
class Callback {
public:
virtual ~Callback() {}
virtual float run(float a, float b) {return 0;}
};
class OP {
public:
Callback *p_add;
Callback *p_sub;
Callback *p_mul;
Callback *p_div;
public:
OP(): p_add(0),p_sub(0),p_mul(0),p_div(0) {}
~OP() {
}
float add_sub_mul_div(float a, float b, Callback* fun) {
if(fun != NULL) {
return fun->run(a, b);
}
printf("add_sub_mul_div error\n");
return 0;
}
};
b) 編寫example.i
重點:這里必須設(shè)置%module(directors="1") example
并且指定回調(diào)類:
%feature("director") Callback;
/* File : example.i */
%module(directors="1") example
%{
#include "example.h"
%}
%feature("director") Callback;
%include "example.h"
c) 編寫runme.java
// runme.java
public class runme {
static {
System.loadLibrary("example");
}
public static void main(String argv[]) {
OP op = new OP();
op.setP_add(new JavaCallbackAdd());
op.setP_sub(new JavaCallbackSub());
op.setP_mul(new JavaCallbackMul());
op.setP_div(new JavaCallbackDiv());
System.out.println(op.add_sub_mul_div(1.3f, 2.2f,op.getP_add()));
System.out.println(op.add_sub_mul_div(1.3f, 2.2f,op.getP_sub()));
System.out.println(op.add_sub_mul_div(1.3f, 2.2f,op.getP_mul()));
System.out.println(op.add_sub_mul_div(1.3f, 2.2f,op.getP_div()));
System.out.println(op.getP_add().run(1.3f, 2.2f));
System.out.println(op.getP_sub().run(1.3f, 2.2f));
System.out.println(op.getP_mul().run(1.3f, 2.2f));
System.out.println(op.getP_div().run(1.3f, 2.2f));
}
}
class JavaCallbackAdd extends Callback
{
public float run(float a, float b) {
return a + b;
}
}
class JavaCallbackSub extends Callback
{
public float run(float a, float b) {
return a - b;
}
}
class JavaCallbackMul extends Callback
{
public float run(float a, float b) {
return a * b;
}
}
class JavaCallbackDiv extends Callback
{
public float run(float a, float b) {
return a / b;
}
}
d) 執(zhí)行以下命令
swig -c++ -java example.i
g++ -Wall -Wl,--kill-at -I "%JAVA_HOME%/include" -I "%JAVA_HOME%/include/win32" -shared -s -o example.dll *.cxx
javac -d . *.java
java -cp . runme
- 類型轉(zhuǎn)換
a) 錯誤的情況
C函數(shù)包裝
%module example
int add_option(const unsigned char *data);
得到的包裝函數(shù):
public static int add_option(SWIGTYPE_p_unsigned_char data)
其中SWIGTYPE_p_unsigned_char完全無法賦值,也就是無法使用
b) 正確的處理
%module example
%include <arrays_java.i>
%apply signed char *INOUT { unsigned char * };
%apply signed char[] { const unsigned char * };
int add_option(const unsigned char *data);
這樣就可以得到包裝函數(shù):
public static int add_option(byte[] data)
c) 更多的類型轉(zhuǎn)換
%include "typemaps.i"
%include "stdint.i"
%include "arrays_java.i"
%include "carrays.i"
typedef int8_t Int8;
typedef uint8_t Uint8;
typedef int16_t Int16;
typedef uint16_t Uint16;
typedef uint32_t Int32;
typedef uint32_t Uint32;
- 兩個常用命令
a) Javah
javah –jni Java類名,可以通過java類生成.h 頭文件
b) javap
javap -s -p Java類名
用來輸出一個Java類的方法的簽名,用于c調(diào)用java類獲得methid的時候使用