一、概述
JNI(全名Java Native Interface)Java native接口,它可以让一个运行在Java虚拟机中的Java代码被调用或者调用native层的代码。简单理解为就是一个连接Java层和Native层的桥梁。
本文我们就来分析一下 Java 与 Native 是如何实现互调的。
关联文章:
二、Native与Java层互调
2.1 用例介绍
实现一个下图所示的功能。
- 下图中有3个按钮,顶部两个按钮一个是让 count 值自增1,由 Java 层实现;第二个按钮是让 count 值自减1,由 Native 层实现;第三个按钮是用于显示当前 count 值的控件。
- 上面两个控件显示的文本是从 JNI 层获取,实现了 Java 对 Native 层的调用。
- 第三个控件数据的更新是通过 JNI 来实现的,实现了 Native 层对 Java 层的调用。
2.2 Java调用Native代码
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("jnitestdemo");
}
TextView textView3;
int count = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView textView = findViewById(R.id.sample_text);
// Java调JNI层获取数据,这部分已经在 [NDK(一):NDK 的集成]部分分析过了。
textView.setText(stringFromJNI());
textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
count++;
textView3.setText(String.valueOf(count));
}
});
TextView textView2 = findViewById(R.id.sample_text_2);
// Java调JNI层获取数据
textView2.setText(staticStringFromJNI());
textView2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Java调JNI层来实现对count的自增操作和对textView3控件的更新。
decreaseCountValue();
}
});
textView3 = findViewById(R.id.sample_text_3);
textView3.setText(String.valueOf(count));
}
public native String stringFromJNI();
public static native String staticStringFromJNI();
// JNI层实现对count的自增操作和对textView3控件的更新
public native void decreaseCountValue();
}
JNI 层:native-libcpp
#include <jni.h>
#include <string>
// 静态注册
extern "C" JNIEXPORT jstring JNICALL
Java_com_elson_jnitestdemo_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
std::string hello = "count+=1";
return env->NewStringUTF(hello.c_str());
}
// 静态注册
extern "C"
JNIEXPORT jstring JNICALL
Java_com_elson_jnitestdemo_MainActivity_staticStringFromJNI(
JNIEnv *env,
jclass clazz) {
std::string hello = "count-=1";
return env->NewStringUTF(hello.c_str());
}
2.3 Native调用Java代码
#include <jni.h>
#include <string>
// Java_decreaseCountValue是动态注册的,所以命名可以不遵寻JNI静态注册的规范。
extern "C"
JNIEXPORT void JNICALL
Java_decreaseCountValue(JNIEnv *env, jobject thiz) {
// 查找某个对象的字段或者方法,都是通过这个类的对象去查找的,即jclass。
// 调用某个对象的方法,都是通过这个对象去调用的,即jobject。
// ----- 获取MainActivity.count字段 -------
// 1.1 先获取MainActivity类的对象。
jclass clazz = env->GetObjectClass(thiz);
// 1.2 通过类对象jclass访问属性字段或方法的id。
jfieldID countFieldId = env->GetFieldID(clazz, "count", "I");
// 1.3 通过字段id获取到字段对应的值。这里获取MainActivity.count的值。
jint oldCount = env->GetIntField(thiz, countFieldId);
// 1.4 更新MainActivity.count的值
env->SetIntField(thiz, countFieldId, oldCount-1);
jint newCount = env->GetIntField(thiz, countFieldId);
// ----- 获取MainActivity.textView3字段 -------
// 2.1 获取textView3字段id。
jfieldID textViewFieldId = env->GetFieldID(clazz, "textView3", "Landroid/widget/TextView;");
// 2.2 需要调用textView.setText()方法,所以先要获取到textView的类对象jclass。
jobject textViewObject = env->GetObjectField(thiz, textViewFieldId);
jclass textViewClazz = env->GetObjectClass(textViewObject);
// 2.3 获取到textView.setText()方法id。
jmethodID methodId = env->GetMethodID(textViewClazz, "setText", "(Ljava/lang/CharSequence;)V");
// ----- 对TextView进行赋值 -------
char buf[64];
sprintf(buf, "%d", newCount);
// 3.1 构建一个jstring。
jstring pJstring = env->NewStringUTF(buf);
// 3.2 调用textView.setText()方法进行赋值。
env->CallVoidMethod(textViewObject, methodId, pJstring);
}
static const JNINativeMethod nativeMethods[] = {
{"decreaseCountValue", "()V", (void *) (Java_decreaseCountValue)},
};
// 添加动态注册入口
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reversed) {
JNIEnv *env = NULL;
// 初始化JNIEnv
if(vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6) != JNI_OK){
return JNI_FALSE;
}
// 找到需要动态动态注册的JNI类
jclass jniClass = env->FindClass("com/elson/jnitestdemo/MainActivity");
if(nullptr == jniClass){
return JNI_FALSE;
}
// 动态注册
env->RegisterNatives(jniClass, nativeMethods, sizeof(nativeMethods)/sizeof(JNINativeMethod));
// 返回JNI使用的版本
return JNI_VERSION_1_6;
}
小结:
- 查找某个对象的字段或者方法,都是通过这个类的对象去查找的,即jclass。
- 调用某个对象的方法,都是通过这个对象去调用的,即jobject。