swig java的最佳实践

1. director

director用于C++回调java。

1.1 director生命周期

使用swig director时,要注意生命周期。如果不注意,则很容易在java gc时崩溃。
要么,在java上需要一个管理类管理java回调函数的生命周期。
要么,把java的生命周期转给C++来管理。

对于后者,需要在编写.i文件时候,加上SWIG_DIRECTOR_OWNED:

%feature("director",assumeoverride=1) ICallback;
SWIG_DIRECTOR_OWNED(ICallback);

其中assumeoverride=1是为了提升性能。与生命周期无关。

1.2 director异常打印

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);
    }
%}

2. 类型映射

2.1 枚举

%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;
}

2.2 void*转byte[]

%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)};

2.3 unsigned char*转byte[]

同理:将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 };

2.4 数组映射

%include "arrays_java.i"

则使用时会把int *当成int []来用,即使用者要通过在java端new int [1]来分配内存的方式来使用。

2.5 指针类型映射

%include 
%template(IntRef) pointer_class;

2.6 数组类型映射

%include 
%template(IntArray) array_class;

2.7 命名空间

等效用法:

namespace std{
%template(UCharVector) vector<unsigned char>;
}

%template(UCharVector) std::vector<unsigned char>;

你可能感兴趣的:(android)