Android JNI常用API函数介绍

9 篇文章 0 订阅

Android JNI常用API函数介绍

一、前言

Android JNI的 普通用法估计很多人都会,网上文章介绍的api感觉不全面或者写得太细了但是并没有汇总显示。

所以感觉有必要写一篇通俗简单、全面的JNI函数API,让JNI初学者对JNI相关内容有个底。

首先要明白JNI函数API定义在哪里?

之后遇到函数API参数不明白的情况,可以进行对比确认。

其实所有jniDemo.cpp调用的api函数都是在 jni.h 文件中定义。

Android13 的Jni.h 目录在:system\extras\module_ndk_libs\libnativehelper\include_jni\jni.h 。

Android Studio 创建JNI工程,点击env的函数方法名,就会跳转到api函数的文件;

比如代码 env->GetStringUTFChars,按住Ctrl+鼠标点击 GetStringUTFChars 方法,就会跳转到源文件jni.h中;

底层具体实现功能的不同cpp函数的API这里不会介绍,只介绍JNI相关的API函数;

看完本文,你会发现JNI实际使用到的API函数不多,也不难。

Android JNI的基础知识介绍,之前已经有介绍,不熟悉的可以先看看:

Android Jni的介绍和简单Demo实现:

https://blog.csdn.net/wenzhi20102321/article/details/136291126

本文主要介绍Java和底层C++数据交互过程的jniDemo.cpp中相关API函数知识,有兴趣的可以看看。

二、主要函数api和相关内容

1、常用api函数汇总

jni需要使用的API函数其实就是在jni.h代码文件中定义。

在Android JNI开发中,常用的API函数包括以下几个:

(1)JNIEnv结构体相关函数:
GetVersion():获取JNI的版本号。
GetJavaVM():获取Java虚拟机实例。
NewGlobalRef():创建全局引用。
DeleteGlobalRef():删除全局引用。

(2)JavaVM结构体相关函数:
AttachCurrentThread():将当前线程附加到Java虚拟机上。
DetachCurrentThread():将当前线程从Java虚拟机上分离。
GetEnv():获取当前线程的JNIEnv实例。

(3)jclass相关函数:
FindClass():查找Java类。
GetMethodID():获取Java方法的ID。
GetStaticMethodID():获取静态Java方法的ID。

(4)jobject相关函数:
NewObject():创建Java对象。
CallVoidMethod():调用Java对象的无返回值方法。
CallObjectMethod():调用Java对象的有返回值方法。
Call<Type>Method():调用Java对象的有返回基本数据类型值方法,比如:CallIntMethod()

(5)字段相关函数:
GetFieldID():获取Java字段的ID。
GetStaticFieldID():获取静态Java字段的ID。
Get<Type>Field():获取Java字段的值。
Set<Type>Field():设置Java字段的值。

(6)数组相关函数:
New<Type>Array():创建Java数组。比如:NewBooleanArray()
Get<Type>ArrayElements():获取Java数组的元素。比如:GetIntArrayElements()
Release<Type>ArrayElements():释放Java数组的元素。

(7)字符串函数
GetStringUTFChars():用于将Java字符串转换为C风格的字符串(以UTF-8编码表示)。
GetStringChars():用于将Java字符串转换为Unicode字符数组。
ReleaseStringUTFChars():用于释放由GetStringUTFChars获得的C字符串。
ReleaseStringChars():用于释放由GetStringChars获得的Unicode字符数组。
NewStringUTF():用于创建一个Java字符串,其内容是以UTF-8编码表示的C风格字符串。
NewString():用于创建一个Java字符串,其内容是Unicode字符数组。

这些函数是JNI开发中经常使用的API,可以用来在Java和C/C++之间进行数据的传递和调用。

上面这个汇总表就是常用的API函数汇总表格;

上面API函数只要用 env->API函数就可以调用:

在JNI库中实现一个Java方法,用于获取JNI版本号。
例如jni.cpp代码中 :
JNIEXPORT jint JNICALL Java_your_package_name_GetVersion_get(JNIEnv *env, jclass clazz) {
    return env->GetVersion(); //调用函数获取JNI的版本号
}

结构体相关的API函数比较少用,其他API函数都是比较简单和常用。

从上面API函数看,有些API是会随Type类型改变的,

所以先要知道JNI里面数据的类型以及API函数的类型字段。

从jni.h文件看Type关键字包含:

Boolean、Byte、Char、Short、Int、Long、Float、Double;

这个是要记住的,很多不同方法的返回值就要用到对应的类型名称函数。

这几个Type关键字就是对应的Java的8中基本数据类型;除了基本数据类型还有String和Object;

下面就对上面提到的api进行展开说明和示例讲解:

2、JNIEnv结构体相关函数:

(1)GetVersion():获取JNI的版本号

这个函数比较简单,没有什么参数并且返回的是int的基本数据类型。

JNIEXPORT jint JNICALL Java_your_package_name_GetVersion_get(JNIEnv *env, jclass clazz) {
    return env->GetVersion(); //调用函数获取JNI的版本号
}
(2)GetJavaVM():获取Java虚拟机实例
#include <jni.h>

JavaVM* g_vm;

//JNI_OnLoad 是JNI默认的声明周期方法,类似Android界面Activity 的onCreate方法
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
    JNIEnv* env;
    if (vm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK) {
        return JNI_ERR;
    }

    // 保存全局的JavaVM实例
    g_vm = vm;

    return JNI_VERSION_1_6;
}

JNIEXPORT void JNICALL Java_com_example_MyClass_nativeMethod(JNIEnv* env, jobject obj) {
    // 在本地方法中使用GetJavaVM函数获取JavaVM实例
    JavaVM* javaVM;
    env->GetJavaVM(&javaVM); //实例化javaVM指针对象

    // 使用 JavaVM 实例进行操作
    // ...

    // 如果需要全局访问JavaVM实例,可以使用全局变量g_vm
    // ...
    
    //如果要调用Java的方法
    //c++调用Java方法:public void cppCallBackMethod(String name, int age)
    //中间获取jclass对象,方法id,定义相关参数变量。。。。
    (*env).CallVoidMethod(obj, cppCallBackMethod, env->NewStringUTF(message), age);
}

从上面代码看到:

JNI的原生流程 JNI_OnLoad方法中,JavaVM* 可以获取到 JNIEnv*;
native对于的cpp映射方法中, JNIEnv* 通过GetJavaVM函数可以获取到JavaVM*;
需要调用Java类的方法时候,需要用到 JNIEnv*;
非映射方法中如果要调用Java类里面的方法,就要用JavaVM获取到JNIEnv,再调用方法。
(3)NewGlobalRef() 和 DeleteGlobalRef()

NewGlobalRef():创建全局引用 ;DeleteGlobalRef():删除全局引用

JNI的NewGlobalRef函数用于创建一个全局引用,以确保Java对象在本地方法中的访问不会受到垃圾回收的影响。下面是一个使用示例:

#include <jni.h>

// 全局引用
jobject globalRef;

JNIEXPORT void JNICALL Java_com_example_MyClass_initialize(JNIEnv *env, jobject obj) {
    // 创建全局引用
    globalRef = (*env)->NewGlobalRef(env, obj);
    // globalRef = obj; //引用会被回收,这样赋值其他方法中调用会报错
}

JNIEXPORT void JNICALL Java_com_example_MyClass_performCallback(JNIEnv *env, jobject obj) {
    // 延时回调Java方法
    jclass cls = (*env)->GetObjectClass(env, globalRef);
    jmethodID methodID = (*env)->GetMethodID(env, cls, "callbackMethod", "()V");
    (*env)->CallVoidMethod(env, globalRef, methodID);
}

JNIEXPORT void JNICALL Java_com_example_MyClass_cleanup(JNIEnv *env, jobject obj) {
    // 删除全局引用
    (*env)->DeleteGlobalRef(env, globalRef);
}

这里可以看到创建的全局引用的jobject是可以在其他方法使用的;

如果需要用到延时较长时间的回调Java代码就可以用“线程+全局引用”的方式。

(4) AttachCurrentThread 和 DetachCurrentThread

JNI的AttachCurrentThread函数用于将当前线程附加到Java虚拟机中,以便在本地线程中调用Java方法。

示例代码如下:

#include <jni.h>

void myNativeMethod(JNIEnv *env) {
    // 在本地线程中调用Java方法
    jclass cls = (*env)->FindClass(env, "com/example/MyClass");
    jmethodID methodID = (*env)->GetStaticMethodID(env, cls, "myMethod", "()V");
    (*env)->CallStaticVoidMethod(env, cls, methodID);
}

void *myThread(void *arg) {
    JavaVM *jvm;
    JNIEnv *env;
    
    // 获取Java虚拟机实例
    if ((*g_vm)->AttachCurrentThread(g_vm, &env, NULL) != JNI_OK) {
        // 错误处理
        return NULL;
    }
    
    // 在本地线程中调用Java方法
    myNativeMethod(env);
    
    // 分离当前线程
    (*g_vm)->DetachCurrentThread(g_vm);
    
    // 线程结束
    return NULL;
}

JNIEXPORT void JNICALL Java_com_example_MyClass_startThread(JNIEnv *env, jobject obj) {
    // 创建并启动新线程
    pthread_t thread;
    pthread_create(&thread, NULL, myThread, NULL);
    pthread_detach(thread);
}
(5)GetEnv 获取环境指针

GetEnv 函数用于获取当前线程的JNIEnv环境指针,以便调用API函数和。

上面获取Java虚拟机示例代码中也是有调用到了 GetEnv 函数,使用示例如下:

JavaVM* g_vm;

//JNI_OnLoad 是JNI默认的声明周期方法,类似Android界面Activity 的onCreate方法
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
    JNIEnv* env;
    if (vm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK) { // GetEnv实例化 env 指针对象
        return JNI_ERR;
    }
}

JNIEXPORT void JNICALL Java_com_example_MyClass_nativeMethod(JNIEnv* env, jobject obj) {
    // 在本地方法中使用GetJavaVM函数获取JavaVM实例
...
}

上面代码可以看到在JNI 生命周期方法中可以通过GetEnv函数获取到JNIEnv*,

nativie的映射方法是自带了JNIEnv*;

有了 JNIEnv* 指针对象就可以调用JNI 的API函数了。

本文所有介绍的API函数都是要使用到JNIEnv*指针对象进行调用的。

3、jclass、jobject、Filed相关函数:

jclass、jobject、Filed相关函数的使用主要都是在C/C++需要调用Java的方法或者修改Java的属性的时候需要的。

示例代码如下:

extern "C" JNIEXPORT jstring JNICALL
Java_com_demo_jnicallback_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject thiz /* this */) {
    std::string hello = "Hello from C++";
    LOGI("stringFromJNI hello = %s", hello.c_str());
    
    //c++调用Java方法:public void cppCallBackMethod(String name, int age)
    jclass mainActivityCls=env->FindClass("com/demo/jnicallback/MainActivity");//获取类对象
    jmethodID cppCallBackMethod = env->GetMethodID(mainActivityCls, "cppCallBackMethod", "(Ljava/lang/String;I)V");
    const char *message = "cppA";
    int age = 10;
    env->CallVoidMethod(thiz, cppCallBackMethod, env->NewStringUTF(message), age);

	//获取属性的fieldId,--》这里就用到了签名类型
	jfieldID ageFid = env->GetFieldID(mainActivityCls,"age","I");
    //获取属性值
    int  age = env->GetIntField(mainActivityThis,ageFid);
    //修改属性值,C++中修改变量值后,Java重新获取打印发现是修改过的
    env->SetIntField(mainActivityThis, ageFid , 11);


    return env->NewStringUTF(hello.c_str());
}

FindClass 都是固定的写法,只是传入的字符串路径不同;

GetMethodID是获取普通方法id,如果Java的是静态方法就用GetStaticMethodID

GetFieldID 和 GetMethodID 函数 都是三个参数:

第一个参数是jclass对象,

第二个参数是Java的方法名称或者变量名称字符串,

第三个参数是方法签名或者变量类型签名。

调用Java方法的函数是:CallXXXMethod,

如果调用的是没有返回值 XXX就是Void,

如果有返回值XXX就是数据类型名称,比如Int,如果是String或者对象类型XXX就是Object

GetXXXField 和 SetXXXField 是获取和设置Java类的属性,XXX也是类型名称或者Object。

NewObject 函数的使用示例:

JNIEXPORT jobject JNICALL Java_com_example_MyNativeClass_createNewObject(JNIEnv *env, jobject obj) {
    // 创建新的Java对象
    jclass cls = env->FindClass("com/example/MyClass"); // 查找类名
    jmethodID methodID = env->GetMethodID(cls, "createNewObject", "()Lcom/example/MyObject;"); // 获取方法ID
    jobject newObject = env->NewObject(cls, methodID); // 创建新对象
    return newObject;
}

Android JNI复杂用法,回调,C++中调用Java方法:

https://blog.csdn.net/wenzhi20102321/article/details/136419405

4、数组和字符串处理

主要相关的API函数:
数组相关函数:
New<Type>Array():创建Java数组。比如:NewBooleanArray()
Get<Type>ArrayElements():获取Java数组的元素。比如:GetIntArrayElements()
Release<Type>ArrayElements():释放Java数组的元素。

字符串函数
GetStringUTFChars():用于将Java字符串转换为C风格的字符串(以UTF-8编码表示)。
GetStringChars():用于将Java字符串转换为Unicode字符数组。
ReleaseStringUTFChars():用于释放由GetStringUTFChars获得的C字符串。
ReleaseStringChars():用于释放由GetStringChars获得的Unicode字符数组。
NewStringUTF():用于创建一个Java字符串,其内容是以UTF-8编码表示的C风格字符串。
NewString():用于创建一个Java字符串,其内容是Unicode字符数组。
示例demo代码:
(1)MainActivity.java代码
package com.demo.jniobject;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

import java.util.Arrays;

public class MainActivity extends AppCompatActivity {

    String TAG = "MainActivity.java";
    static {
        System.loadLibrary("native-lib");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.i(TAG, "onCreate");

        TextView tv = findViewById(R.id.sample_text);
        //返回cpp的字符串
        String jniString = stringFromJNI();
        Log.i(TAG, "onCreate jniString = " + jniString);
        tv.setText(jniString);

        //传递int值,cpp 返回int值
        int intToJNIBack = intToJNI(16);
        Log.i(TAG, "onCreate intToJNIBack = " + intToJNIBack);

        //传递String值,cpp 返回String值
        String stringToJNIBack = stringToJNI("liwenzhi");
        Log.i(TAG, "onCreate stringToJNIBack = " + stringToJNIBack);
        
        //传递String和int值,cpp返回String值
        String stringAndIntToJNIBack = stringAndIntToJNI("陈wang",18);
        Log.i(TAG, "onCreate stringAndIntToJNIBack = " + stringAndIntToJNIBack);
        
        //传递String数值和int数组值,cpp返回String数值的值
        String[] listStringAndListIntToJNIBack = listStringAndListIntToJNI(new String[] {"姚pengtao", "朱dejiu","周fuping"},new int[] {20,21,30});
        Log.i(TAG, "onCreate listStringAndListIntToJNIBack = " + Arrays.asList(listStringAndListIntToJNIBack));

        Log.i(TAG, "onCreate End jniString = " + jniString);
    }


    //Java到cpp并且获取返回数据方法
    public native String stringFromJNI(); //返回cpp的字符串
    public native int intToJNI(int age); //传递int值,cpp 返回int值
    public native String stringToJNI(String name); //传递String值,cpp 返回String值
    public native String stringAndIntToJNI(String name, int age); //传递String和int值,cpp返回String值
    public native String[] listStringAndListIntToJNI(String[] names, int[] ages); //传递String数值和int数组值,cpp返回String数值的值

}

上面这个Java代码就包含很字符串已经数值的数据传递;

具体的处理过程可以看看cpp代码。

(2)native-lib.cpp 代码
#include <jni.h>
#include <string>

#include <android/log.h>
#define LOG_TAG "native-lib.cpp"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)

//Java 方法: public native String stringFromJNI()
extern "C" JNIEXPORT jstring JNICALL
Java_com_demo_jniobject_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    LOGI("stringFromJNI hello = %s", hello.c_str());
    return env->NewStringUTF(hello.c_str());
}

//Java 方法: public native int intToJNI(int age)
extern "C" JNIEXPORT jint JNICALL
Java_com_demo_jniobject_MainActivity_intToJNI(JNIEnv *env, jobject thiz, jint age) {
    int cppAge = age +10;
    return cppAge;
}

//Java 方法:public native String stringToJNI(String name)
extern "C" JNIEXPORT jstring JNICALL
Java_com_demo_jniobject_MainActivity_stringToJNI(JNIEnv *env, jobject thiz, jstring name) {
return name;//这里直接返回,想要修改字符串内容可以看看下面代码
}

//Java 方法:public native String stringAndIntToJNI(String name, int age)
extern "C" JNIEXPORT jstring JNICALL
Java_com_demo_jniobject_MainActivity_stringAndIntToJNI(JNIEnv *env, jobject thiz, jstring name,jint age) {

    // 将jstring转换为C字符串
    const char* c_str1 = env->GetStringUTFChars(name, nullptr);
    // 进行字符串拼接,不能像Java一样,"" + 5,这样会报错,要先转换类型
    // 将jint转换为C++字符串
    std::string numStr = std::to_string(age);
    std::string result = std::string(c_str1) + "cpp 拼接 age = " + numStr;

    LOGD("stringAndIntToJNI name = %s", c_str1);
    // 释放GetStringUTFChars函数申请的资源
    env->ReleaseStringUTFChars(name, c_str1);

    // 将C字符串转换为jstring
    return env->NewStringUTF(result.c_str());
}

//Java 方法:public native String[] listStringAndListIntToJNI(String[] names, int[] ages)
extern "C" JNIEXPORT jobjectArray JNICALL
Java_com_demo_jniobject_MainActivity_listStringAndListIntToJNI(JNIEnv *env, jobject thiz,jobjectArray names, jintArray ages) {
    // 获取数组长度
    jint length = env->GetArrayLength(names);

    // 创建一个新的数组,用于存储修改后的数据
    jobjectArray newArray = env->NewObjectArray(length, env->FindClass("java/lang/String"), nullptr);

    // 获取原始int数组的指针
    jint *originalArray = env->GetIntArrayElements(ages, nullptr);

    // 遍历原始数组
    for (int i = 0; i < length; i++) {
        // 获取原始数组元素
        jstring element = (jstring) env->GetObjectArrayElement(names, i);
        jint elementInt = originalArray[i] + 2;
        std::string numStr = std::to_string(elementInt);

        // 将原始字符串转换为新的字符串
        const char *c_str = env->GetStringUTFChars(element, nullptr);
        std::string modifiedStr = "Modified: ";
        modifiedStr += c_str;
        modifiedStr += ",age =";
        modifiedStr += numStr;
        //释放String
        env->ReleaseStringUTFChars(element, c_str);
        LOGE("listStringAndListIntToJNI modifiedStr = %s", modifiedStr.c_str());
        // 创建一个新的字符串对象,并将其设置到新的数组中
        jstring newElement = env->NewStringUTF(modifiedStr.c_str());
        env->SetObjectArrayElement(newArray, i, newElement);
    }

    // 释放原始int数组的指针
    env->ReleaseIntArrayElements(ages, originalArray, 0);

    // 返回修改后的数组
    return newArray;

}

里面的代码是不难,但是没写过的,就不知道用哪些api,字符串怎么修改,怎么拼接,打印;

每个Type类型没必要所有的都用一遍,会用一个其他的都是差不多的,变通一下就行;

除了基本类型的Type,如果用到其他Java对象也是和String一样写法就行。

复杂用法可以看看我之前写的文章:

Android JNI 复杂数据demo ,字符串、数组对象等数据操作讲解:

https://blog.csdn.net/wenzhi20102321/article/details/136511145

三、其他

1、主要函数API总结

主要API内容:

(1)JNIEnv 结构体相关函数
(2)JavaVM 结构体相关函数
(3)jclass 相关函数
(4)jobject 相关函数
(5)字段相关函数
(6)数组相关函数
(7)字符串函数

其实主要的JNI API 函数就上面这些相关的内容;大部分会用,那么JNI 的API 就没啥难度了。

其他的API函数可以看jni.h文件,但是这个文件是没有注释的,根据字面意义理解就差不多了。

除了JNIEnv和JavaVM使用的获取使用比较少,其他API使用的还是比较多的;

但是JNI API函数的调用基本都是基于JNIEnv指针对象的,

native对应的映射方法默认就有JNIEnv指针对象和类对象jobject 。

JNI其他API接收文章:JNI的常用方法的中文API:

https://blog.csdn.net/szembed/article/details/126936199

2、在线查看jni.h源码:

地址:

http://aospxref.com/android-13.0.0_r3/xref/system/extras/module_ndk_libs/libnativehelper/include_jni/jni.h

系统中其他文件的代码也是可以用这个网址进行查看。

除了system目录下的jni.h ,在prebuilts目录下同样是有很多jni.h

./prebuilts/jdk/jdk9/linux-x86/include/jni.h
./prebuilts/jdk/jdk9/darwin-x86/include/jni.h
./prebuilts/jdk/jdk17/linux-x86/include/jni.h
./prebuilts/jdk/jdk17/darwin-x86/include/jni.h
./prebuilts/jdk/jdk17/windows-x86/include/jni.h
./prebuilts/jdk/jdk8/linux-x86/include/jni.h
./prebuilts/jdk/jdk8/darwin-x86/include/jni.h
./prebuilts/jdk/jdk11/linux-x86/include/jni.h
./prebuilts/jdk/jdk11/darwin-x86/include/jni.h
./prebuilts/jdk/jdk11/darwin-arm64/include/jni.h

里面的代码除了格式化形式不同,代码内容大部分都是一样的,看其中一个就行。

3、JNI cpp中 JavaVM 和 JNIEnv的关系:

Jni是Java Native Interface的缩写,是Java提供的一种机制,用来实现Java与其他编程语言的交互。

JavaVM(Java Virtual Machine)是Java虚拟机的实例,它负责解释和执行Java字节码。
JNIEnv(Java Native Interface Environment)是一种数据结构,用于在本地方法中访问Java虚拟机的接口。

JavaVM是整个Java虚拟机的实例,它在程序启动时创建,并负责加载、解释和执行Java字节码。
JNIEnv是在本地方法中访问Java虚拟机的接口,它通过JavaVM实例获取,并提供了一系列与Java虚拟机交互的函数。
通过JNIEnv,本地方法可以调用Java方法、访问Java对象和数组,并进行异常处理等操作。

因此,可以说JNIEnv是JavaVM的一部分,通过JNIEnv可以与Java虚拟机进行交互。
在本地方法中,可通过JNIEnv访问Java对象、方法和数组等,并调用相应的函数与Java虚拟机进行通信。

需要注意的是,JavaVM*是线程安全的,可以在多线程环境下使用。*
*但是,JNIEnv*是线程特定的,每个线程需要通过JavaVM*获取自己的JNIEnv*才能进行操作。

3、JNI cpp中 的 JNI_OnLoad 函数

JNI_OnLoad函数是一个可选函数,当JNI库被加载时会被自动调用。

*JNI_OnLoad使用场景一般是:全局变量设置 或者 动态加载JNI。

(1)全局变量设置

在该函数中,可以将传入的JavaVM实例保存为全局变量,以便在其他本地方法中使用。

(1) 通过JNIEnv*获取当前线程的JNIEnv*:
  在本地方法中,可以通过JNIEnv*的GetJavaVM函数获取当前线程的JavaVM*实例。然后可以使用JNIEnv*进行与Java代码的交互,如调用Java方法、创建Java对象等。
(2) 执行与Java代码交互的操作:
  使用JNIEnv*进行与Java代码的交互操作,例如调用Java方法、创建Java对象、访问Java对象等。
(3) 在JNI_OnUnload函数中进行清理操作(可选):
  JNI_OnUnload函数在JNI库被卸载时被自动调用,可以在该函数中进行一些清理操作,例如释放资源或取消全局引用。

上面使用 GetJavaVM() API函数示例和NewGlobalRef() API函数示例 都是有设置全局变量的API 使用示例。

(2)动态注册JNI方法

动态注册主要代码如下:

//包名+类名字符串定义:
const char *mainactivity_class_name = "com/demo/jnistatic/MainActivity";

// 重点:定义类名和函数签名,如果有多个方法要动态注册,在数组里面定义即可
static const JNINativeMethod methods[] = {
        {"stringFromJNI", "()Ljava/lang/String;", (void *) cpp_stringFromJNI},
        {"intFromJNI",    "(I)I",                 (void *) cpp_intFromJNI},
};


// 定义注册方法
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
    LOGD("动态注册");
    JNIEnv *env;
    if ((vm)->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
        LOGD("动态注册GetEnv  fail");
        return JNI_ERR;
    }

    // 获取类引用
    jclass clazz = env->FindClass(mainactivity_class_name);

    // 注册native方法
    jint regist_result = env->RegisterNatives(clazz, methods,
                                              sizeof(methods) / sizeof(methods[0]));
    if (regist_result) { // 非零true 进if
        LOGD("动态注册 fail regist_result = %d", regist_result);
    } else {
        LOGI("动态注册 success result = %d", regist_result);
    }
    return JNI_VERSION_1_6;
}

4、Android JNI静态注册和动态注册方法详解

https://blog.csdn.net/wenzhi20102321/article/details/136700209

5、Android Jni的介绍和简单Demo实现

https://blog.csdn.net/wenzhi20102321/article/details/136291126

6、JNI复杂用法,回调,C++中调用Java方法

https://blog.csdn.net/wenzhi20102321/article/details/136419405

  • 26
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

峥嵘life

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值