Android中Java调用C++的两种方法—静态注册和动态注册

1.静态注册

通过将 Java 方法和对应的 C++ 函数的名称严格按照 JNI 的命名规则进行匹配来实现的。这种方法比较简单直观,但函数名称必须按照特定的规则命名。

  • 在Activity中声明JNI函数接口
  • 在cpp 文件中生成对应的c++的函数实现

示例:

在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());
}

2.动态注册

动态注册方法允许在运行时注册 C++ 函数到 Java 方法。这种方法灵活性更高,不需要严格遵循命名规则。

主要步骤:

  • 在Activity中声明JNI函数接口
  • 在cpp 文件中生成对应的c++的函数实现(名字随意)
  • 生成对应的动态注册表
  • 提供运行时注册函数

示例如下:

声明接口

在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"
    这是 Java 方法的名称,必须与 Java 类中声明的本地方法名称一致。
  • signature"()Ljava/lang/String;"
    这是方法的签名,描述了方法的参数和返回类型:
    • () 表示这个方法没有参数。
    • Ljava/lang/String; 表示这个方法返回一个 java.lang.String 类型的对象。
  • fnPtr(void *)getStringFromNative
    这是一个指向本地函数的指针,即 C/C++ 实现的方法 getStringFromNative 的地址。
    • (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 方法,然后被调用。

3.完整示例代码

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 <jni.h>
#include <string>
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++ 函数对应起来。

动态注册:灵活性更高,允许在运行时注册方法,不受命名规则限制。

效果展示:

在这里插入图片描述

4.附录:JNI中的签名(signature)

JNI(Java Native Interface)的签名(signature)用于描述 Java 方法的参数和返回类型。签名字符串是方法签名的编码表示,用于在本地代码和 Java 虚拟机之间传递方法的类型信息。以下是 JNI 签名的详细解释。

基本类型签名

每种 Java 基本类型都有一个对应的签名字符:

  • Z - boolean
  • B - byte
  • C - char
  • S - short
  • I - int
  • J - long
  • F - float
  • D - double
  • V - 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 的方法。

示例解析

以下是一些示例签名的解析:

示例 1

Java 方法:

public void myMethod();

JNI 签名:

()V

解释:

  • () 表示方法没有参数。
  • V 表示方法返回 void 类型。
示例 2

Java 方法:

public int add(int a, int b);

JNI 签名:

(II)I

解释:

  • (II) 表示方法有两个 int 参数。
  • I 表示方法返回 int 类型。
示例 3

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 类型。
示例 4

Java 方法:

public int[] processArray(int[] input);

JNI 签名:

([I)[I

解释:

  • ([I) 表示方法有一个 int[] 参数。
  • [I 表示方法返回一个 int[] 类型。
  • 10
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Trump. yang

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

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

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

打赏作者

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

抵扣说明:

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

余额充值