通过将 Java 方法和对应的 C++ 函数的名称严格按照 JNI 的命名规则进行匹配来实现的。这种方法比较简单直观,但函数名称必须按照特定的规则命名。
示例:
在MainActivity中声明 public native String stringFromJNI();
在native-lib.cpp中声明具体的实现,名字有严格的要求,格式为Java+包名+函数名
实现如下:
extern "C" JNIEXPORT jstring JNICALL
Java_com_marxist_firstjni_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
std::string hello = "Java 调用 C++ 静态注册方法";
return env->NewStringUTF(hello.c_str());
}
动态注册方法允许在运行时注册 C++ 函数到 Java 方法。这种方法灵活性更高,不需要严格遵循命名规则。
示例如下:
在MainActivity中声明 getStringFromNative
在native-lib.cpp中声明具体的实现,函数名字不做具体的严格要求
jstring getStringFromNative(JNIEnv* env, jobject /* this */) {
std::string hello = "Java 调用 C++ 动态注册方法";
return env->NewStringUTF(hello.c_str());
}
static JNINativeMethod methods[] = {
{"getStringFromNative", "()Ljava/lang/String;", (void *)getStringFromNative}
};
JNINativeMethod 是一个结构体,用于表示 Java 本地方法和其对应的 C/C++ 实现。它的定义如下:
typedef struct {
const char* name; //Java 方法的名称。
const char* signature; //Java 方法的签名,用于描述方法的参数和返回类型。
void* fnPtr; //指向实现该方法的本地函数的指针。
} JNINativeMethod;
methods[] 为数组,这个数组用于动态注册 JNI 方法。对应的元素为
{"getStringFromNative", "()Ljava/lang/String;", (void *)getStringFromNative}
这个元素具体表示一个 Java 方法与其对应的本地实现的关系:
name:"getStringFromNative"signature:"()Ljava/lang/String;"() 表示这个方法没有参数。Ljava/lang/String; 表示这个方法返回一个 java.lang.String 类型的对象。fnPtr:(void *)getStringFromNativegetStringFromNative 的地址。
(void *) 是将函数指针转换为 void* 类型,符合 JNINativeMethod 结构体的定义。// 注册本地方法
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* env;
// 寻找JEnv
if (vm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK) {
return JNI_ERR;
}
//寻找 加载native的类对象 , 从包名开始 用/分割,Android Studio有动态提示
jclass clazz = env->FindClass("com/marxist/firstjni/MainActivity");
if (clazz == nullptr) {
return JNI_ERR;
}
//根据动态注册表的参数进行动态注册
if (env->RegisterNatives(clazz, methods, sizeof(methods) / sizeof(methods[0])) < 0) {
return JNI_ERR;
}
return JNI_VERSION_1_6;
}
当JVM运行的时候,将会注册 C++ 函数到 Java 方法,然后被调用。
MainActivity:
package com.marxist.firstjni;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import com.marxist.firstjni.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
// 加载C++库
static {
System.loadLibrary("firstjni");
}
private ActivityMainBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
TextView tv = binding.sampleText;
tv.setText(stringFromJNI()+"\n"+getStringFromNative());
}
//声明一个JNI函数 接口
public native String stringFromJNI();
public native String getStringFromNative();
}
native-lib.cpp:
#include
#include
extern "C" JNIEXPORT jstring JNICALL
Java_com_marxist_firstjni_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
std::string hello = "Java 调用 C++ 静态注册方法";
return env->NewStringUTF(hello.c_str());
}
jstring getStringFromNative(JNIEnv* env, jobject /* this */) {
std::string hello = "Java 调用 C++ 动态注册方法";
return env->NewStringUTF(hello.c_str());
}
// 动态注册表
static JNINativeMethod methods[] = {
{"getStringFromNative", "()Ljava/lang/String;", (void *)getStringFromNative}
};
//JVM启动的时候调用的函数
jint JNI_OnLoad(JavaVM *vm,void* reserved){
JNIEnv *env = NULL;
if (vm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK) {
return JNI_ERR;
}
jclass clazz = env->FindClass("com/marxist/firstjni/MainActivity"); //调用native 方法的Java 类
if (clazz == nullptr) {
return JNI_ERR;
}
if (env->RegisterNatives(clazz, methods, sizeof(methods) / sizeof(methods[0])) < 0) {
return JNI_ERR;
}
return JNI_VERSION_1_6;
}
静态注册:简单、直接,通过严格的命名规则将 Java 方法与 C++ 函数对应起来。
动态注册:灵活性更高,允许在运行时注册方法,不受命名规则限制。
效果展示:

JNI(Java Native Interface)的签名(signature)用于描述 Java 方法的参数和返回类型。签名字符串是方法签名的编码表示,用于在本地代码和 Java 虚拟机之间传递方法的类型信息。以下是 JNI 签名的详细解释。
每种 Java 基本类型都有一个对应的签名字符:
Z - booleanB - byteC - charS - shortI - intJ - longF - floatD - doubleV - void对象类型的签名由 L 开头,后跟类的全限定名,并以分号 ; 结尾。例如:
Ljava/lang/String; 表示 java.lang.String 类。Ljava/util/List; 表示 java.util.List 类。数组类型的签名由一个或多个左方括号 [ 开头,后跟元素类型的签名。例如:
[I 表示 int[] 数组。[Ljava/lang/String; 表示 java.lang.String[] 数组。[[D 表示 double[][] 数组。方法签名由参数类型签名列表和返回类型签名组成,参数类型签名列表用圆括号 () 括起来,返回类型签名放在括号之后。例如:
()V 表示一个没有参数且没有返回值的方法。(I)V 表示一个接受一个 int 参数且没有返回值的方法。(Ljava/lang/String;)I 表示一个接受一个 java.lang.String 参数并返回一个 int 的方法。([I)Ljava/lang/String; 表示一个接受 int[] 参数并返回 java.lang.String 的方法。以下是一些示例签名的解析:
Java 方法:
public void myMethod();
JNI 签名:
()V
解释:
() 表示方法没有参数。V 表示方法返回 void 类型。Java 方法:
public int add(int a, int b);
JNI 签名:
(II)I
解释:
(II) 表示方法有两个 int 参数。I 表示方法返回 int 类型。Java 方法:
public String getMessage(String prefix);
JNI 签名:
(Ljava/lang/String;)Ljava/lang/String;
解释:
(Ljava/lang/String;) 表示方法有一个 java.lang.String 参数。Ljava/lang/String; 表示方法返回一个 java.lang.String 类型。Java 方法:
public int[] processArray(int[] input);
JNI 签名:
([I)[I
解释:
([I) 表示方法有一个 int[] 参数。[I 表示方法返回一个 int[] 类型。