一般情况下,我们都是用 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分,麻烦有积分的就用这个下载吧,没有积分的可以用下面的另一个链接。谢谢!)