director用于C++回调java。
使用swig director时,要注意生命周期。如果不注意,则很容易在java gc时崩溃。
要么,在java上需要一个管理类管理java回调函数的生命周期。
要么,把java的生命周期转给C++来管理。
对于后者,需要在编写.i文件时候,加上SWIG_DIRECTOR_OWNED:
%feature("director",assumeoverride=1) ICallback;
SWIG_DIRECTOR_OWNED(ICallback);
其中assumeoverride=1是为了提升性能。与生命周期无关。
director有一个特性是在回调的java抛异常时,如果java没有捕获,则会在c++里转抛。这个其实也是有办法关闭的。这里介绍的是异常打印,因为director默认没调异常打印,不利于排查问题。至于刚才提到的关闭异常,就是注释掉最后的throw Swig::DirectorException(jenv, $error);即可。
%{
static inline void printException(JNIEnv * jenv, jthrowable throwable){
if (throwable) {
jclass throwclz = jenv->FindClass("java/lang/Throwable");
if (throwclz) {
jmethodID printStackMethod = jenv->GetMethodID(throwclz, "printStackTrace", "()V");
if (printStackMethod) {
jenv->CallNonvirtualVoidMethod(throwable, throwclz, printStackMethod);
}
}
}
}
%}
%feature("director:except") %{
jthrowable $error = jenv->ExceptionOccurred();
if ($error) {
jenv->ExceptionClear();
printException(jenv, $error);
throw Swig::DirectorException(jenv, $error);
}
%}
%include
%javaconst(1);
javaconst带和不带的区别就是带上后看代码更直观,并且不需要再调用下jni。
带上的话:
public final static int EnumA = 0;
public final static int EnumB = EnumA + 1;
不带javaconst的话:
public final static int EnumA = xxxJNI.EnumA_get();
public final static int EnumB = xxxJNI.EnumB_get();
enumtypeunsafe带的话是:
public final class EnumXX {
public final static int EnumA = 0;
public final static int EnumB = EnumA + 1;
public final static int EnumC = EnumB + 1;
enumtypeunsafe不带的话是:
public final class EnumXX {
public final static EnumXX EnumA = new EnumXX("EnumA");
public final static EnumXX EnumB = new EnumXX("EnumB");
public final static EnumXX EnumC = new EnumXX("EnumC");
public final int swigValue() {
return swigValue;
}
public String toString() {
return swigName;
}
public static EnumXX swigToEnum(int swigValue) {
if (swigValue < swigValues.length && swigValue >= 0 && swigValues[swigValue].swigValue == swigValue)
return swigValues[swigValue];
for (int i = 0; i < swigValues.length; i++)
if (swigValues[i].swigValue == swigValue)
return swigValues[i];
throw new IllegalArgumentException("No enum " + EnumXX.class + " with value " + swigValue);
}
private EnumXX(String swigName) {
this.swigName = swigName;
this.swigValue = swigNext++;
}
private EnumXX(String swigName, int swigValue) {
this.swigName = swigName;
this.swigValue = swigValue;
swigNext = swigValue+1;
}
private EnumXX(String swigName, EnumXX swigEnum) {
this.swigName = swigName;
this.swigValue = swigEnum.swigValue;
swigNext = this.swigValue+1;
}
private static EnumXX[] swigValues = { EnumA, EnumB, EnumC };
private static int swigNext = 0;
private final int swigValue;
private final String swigName;
}
%include various.i
//按照 char * BYTE的规则来处理void*,即将void* -> byte[]
%apply char* BYTE {void*};
//按照char *STRING, size_t LENGTH的规则,来处理const void *, int,即将两个参数转成一个参数byte[]
%apply (char *STRING, size_t LENGTH) {(const void* pData, int nSize)};
同理:将unsigned char *处理成byte[],需要自己建立内存映射:
%typemap(jni) unsigned char *UBYTE "jbyteArray"
%typemap(jtype) unsigned char *UBYTE "byte[]"
%typemap(jstype) unsigned char *UBYTE "byte[]"
%typemap(in) unsigned char *UBYTE {
$1 = (unsigned char *) JCALL2(GetByteArrayElements, jenv, $input, 0);
}
%typemap(argout) unsigned char *UBYTE {
JCALL3(ReleaseByteArrayElements, jenv, $input, (jbyte *) $1, 0);
}
%typemap(javain) unsigned char *UBYTE "$javainput"
/* Prevent default freearg typemap from being used */
%typemap(freearg) unsigned char *UBYTE ""
//使用
%apply unsigned char *UBYTE { unsigned char *value };
%include "arrays_java.i"
则使用时会把int *当成int []来用,即使用者要通过在java端new int [1]来分配内存的方式来使用。
%include
%template(IntRef) pointer_class;
%include
%template(IntArray) array_class;
等效用法:
namespace std{
%template(UCharVector) vector<unsigned char>;
}
%template(UCharVector) std::vector<unsigned char>;