NDK(四):Native与Java互调

文章介绍了JavaNativeInterface(JNI)如何作为桥梁实现Java层与Native层的互调。通过一个实例展示了Java调用Native代码(包括静态注册方法)以及Native调用Java代码的过程,包括修改Java对象的字段值和调用对象方法。
摘要由CSDN通过智能技术生成

一、概述

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。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值