1. 程式人生 > >JNI呼叫C++類的方式

JNI呼叫C++類的方式

在Jni中,提供的最直接的呼叫c/c++的方式是函式。但是在很多時候,我們拿到手的並不一定是純c的程式碼。而且,面向物件的程式結構方式對於模組化和程式的解耦和有著非常積極的作用。用一個c++的類與Java類相對應,可以思路更清晰的建立連線。
如果需要例項的童鞋請戳這裡http://download.csdn.net/detail/xiaohan2909/9307375
OK,廢話不多說了,下面就來講解具體的實現方法:

1.用地址抓住c++物件的把柄

假設我們要呼叫的c++物件定義如下:

class Person{
private:
    int age;
    const char* name;
public
: Person(); void init(int,const char*); void say_info(); void writeFile(const char* path_name,const char* content); };

首先,我們不僅要想要產生物件,而且要在需要的時候能夠找到這個物件,這就需要在java中與c++中建立一點聯絡,這個聯絡通過傳址來實現。也就是說,如果我們可以在java中持有一個C++物件,首先要設法呼叫該物件的建構函式,開闢一塊記憶體,產生一個物件,然後再把這個物件存在的地址記錄到java物件裡面,這樣下次就可以通過這個地址來找到c++的物件了。
現在問題來了,到底咋存呢?
答案是用一個long型就夠了,其實就是拿它作指標用。
然後我們要有一個函式來建立本地物件並且返回它的地址,所以這個類目前看上去像這樣:

public class JniPerson {
    //儲存c++類的地址
    long nativePerson;
    //建構函式
    public JniPerson(){
        nativePerson = createNativeObject();
    }
    /**本地方法:建立c++物件並返回地址*/
    private native long createNativeObject();
}

為了防止忘掉建立這個物件,我把它的建立寫在了建構函式裡。
然後就是如何獲得這個地址,只需要在對映本地方法的函式中這麼寫:

JNIEXPORT jlong JNICALL Java_ouc_sei_test_JniPerson_createNativeObject
(JNIEnv * env, jobject
obj){
jlong result; result =(jlong) new Person(); return result; }//為了方便觀察我把這個過程拆了,實際上這裡一句就夠了

從這裡我們可以看出,C++中物件的建構函式並不是沒有返回值,而是省略了不寫而已,任何c++物件的建構函式的返回值實際上是這個物件的指標。然後我們使用強制轉換把這個指標轉換為jlong然後返回給了java物件中的nativePerson來儲存這個物件的地址。

2呼叫物件的方法

有了這個物件的地址,我們就可以在java中很方便的呼叫該物件的方法了。例如我們要呼叫該物件的init(int,const char*);方法,這個方法的作用是給私有屬性賦值。java class裡的本地方法這麼寫:

private native void nativeInitPerson(long personAddr,int age,String name);

請留意一下第一個引數,這就是物件的地址。然後我們會利用它來傳入兩個變數(在java中這種風格的的函式叫做代理)。
呼叫時的寫法:

JNIEXPORT void JNICALL Java_ouc_sei_test_JniPerson_nativeInitPerson
  (JNIEnv * env, jobject obj, jlong thiz, jint jage, jstring jname){
    const char* name_str = env->GetStringUTFChars(jname,0);
    //物件指標呼叫方法
    ((Person*)thiz)->init(jage,name_str);
}

name是個String型,所以需要轉換一下,而age可以直接與cpp對接。
加註釋下面的一句是重點,thiz是個jlong型,其實就是我們傳入的物件地址。我們強制把它轉換回了指向該物件的指標,並且呼叫該物件的方法,這樣就實現了對c++類方法的呼叫。

值得一提的是,在opencv的java程式碼中廣泛使用了這種方式來呼叫c++的類庫。