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分,麻煩有積分的就用這個下載吧,沒有積分的可以用下面的另一個連結。謝謝!)