1. 程式人生 > >Android:JNI呼叫C++自定義類的詳細方法

Android:JNI呼叫C++自定義類的詳細方法

一般情況下,我們都是用 JNI 呼叫 C++ 的某個方法的程式碼,包括直接使用 android studio 生成的程式碼也是如此。但有時我們需要新建、或者得到的是 C++ 的一個自定義類,在呼叫時就不能像呼叫 C++ 方法那樣了,查閱了一部分其他人的部落格,寫的都比較籠統、模糊,對沒接觸過這塊知識的人來說很不友好,故參考了幾篇較好的部落格,以下介紹具體使用方法。

1.建立JNI工程 

為了方便,可以讓系統幫我們自動生成一個 CMakeLists.txt 檔案,以及載入本地庫的程式碼。在建立 android 工程時,勾選頁面下方的 “include C++ support” 選項,後面的按照預設的選項一直點選 next 就可以。 

我們此次使用 C++ 類的目的是使用 C++ 物件的一些方法,為了簡化,我們擬建立一個 Person 物件,並呼叫它的設定、獲取年齡的方法。


2.建立java類

新建一個 java 類,名稱為 JniPerson,並編寫我們需要的功能,我們需要將此類與 C++ 程式碼產生聯絡,我們可以通過傳遞物件的地址來實現。也就是說,如果我們可以在 java 中持有一個 C++ 物件,首先要設法呼叫該物件的建構函式,開闢一塊記憶體,產生一個物件,然後再把這個物件存在的地址記錄到 java 物件裡面,這樣下次就可以通過這個地址來找到 C++ 的物件了。 此處我們用一個 long 型數值作為指標。 然後我們要有一個函式來建立本地物件並且返回它的地址,程式碼如下:

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

 

下面的 native 方法在剛剛建立時,是標紅的,先不理會,我們在下一步解決。上方的public方法為java類自身的方法,其函式體呼叫了下方定義的native方法。

3. 新增C++類

對於系統自動生成的那個在 cpp 資料夾下的 native-lib.cpp 檔案,先不理會。我們可以將自己的 C++ 類匯入到這個 cpp 資料夾下。當然也可以自己建立,這裡,我們新建一個 C++ 類,在 cpp 資料夾上點滑鼠右鍵,選擇 new->C++ class,在彈出的對話方塊中輸入自定義 C++ 類的名稱,此處我們填寫 Person,點選OK,系統會幫我們建立 Person.cpp 檔案和 Person.h 檔案,修改 Person.h 檔案為以下內容:

#ifndef CPPJNITEST_PERSON_H
#define CPPJNITEST_PERSON_H

class Person {
private:
    int age;
public:
    Person();
    int getAge();
    void setAge(int age);
};

#endif //CPPJNITEST_PERSON_H

 

接著我們修改 Person.cpp 為以下內容:

#include "Person.h"
#include <jni.h>

extern "C"
JNIEXPORT jlong JNICALL
Java_com_example_lcq_cppjnitest_JniPerson_createNativeObject(JNIEnv *env, jobject obj) {
    jlong result;
    result =(jlong) new Person();
    return result;
}

extern "C"
JNIEXPORT void JNICALL
Java_com_example_lcq_cppjnitest_JniPerson_setAge(JNIEnv *env, jobject obj, jlong addr, jint age) {
    //物件指標呼叫方法
    ((Person*)addr)->setAge(age);
}

extern "C"
JNIEXPORT jint JNICALL
Java_com_example_lcq_cppjnitest_JniPerson_getAge(JNIEnv *env, jobject obj, jlong addr) {
    return ((Person*)addr)->getAge();
}


int Person::getAge() {
    return this->age;
}

void Person::setAge(int age) {
    this->age = age;
}

Person::Person() {

}

 

這樣,我們一方面實現了 Person.h 中的方法,另一方面實現了在上一個步驟中標紅的為實現的 native 程式碼。對於程式碼中那串長長的函式名,有個簡便的生成方法:可以在之前建立的 JniPerson 類中,將滑鼠定位在標紅的 JNI 方法後,按 Alt+Enter,會提示建立 JNI 方法,這樣就直接可以生成那一串長長的函式名,再將這串函式名複製過來用就行(有時會在函式名後加一些額外的字元,將額外字元刪除即可)。

 - createNativeObject 函式用於建立物件,並將所創物件的地址返回,便於與 java 建立聯絡。

 - setAge、getAge方法中,引數 addr 即為 createNativeObject 返回的 long 型別的地址,可以通過 java 將這個地址傳進 C++ 中,利用 (Person*)addr,將 addr 強制轉換為指向當初建立的物件的指標,然後就可以呼叫該物件的”自帶“方法了。

 - 最下面的幾個方法,則是實現了 Person.h 中申明的方法。

4.修改CMakelists.txt檔案

由於系統已經幫我們建立了這個檔案,所以省去了我們很多事情,直接在add_library那兒,把我們自定義的Person.cpp新增進去就可以了,原來的native-lib.cpp,可以刪掉。

5.函式呼叫

在MainActivity中,我們建立java物件JniPerson,並呼叫它的方法,它的方法再去呼叫C++類的方法。

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        JniPerson mJniPerson = new JniPerson();
        mJniPerson.setAge(68);
        String mAge =  mJniPerson.getAge() + "";

        TextView tv = (TextView) findViewById(R.id.sample_text);
        tv.setText(mAge);
    }


這樣,整個呼叫過程就結束了。


原始碼下載地址:

https://download.csdn.net/download/chaoqiangscu/10715696 (最低只能設定1分,麻煩有積分的就用這個下載吧,沒有積分的可以用下面的另一個連結。謝謝!)

https://github.com/chaoqiangscu/CppJNITest