JNI 使用

文章详细介绍了JavaNativeInterface(JNI)如何在Java和C/C++之间建立桥梁,包括定义Native方法、生成头文件、实现本地代码、数据类型转换、JNI方法、签名、JNIEnv结构以及一个实际的JNI实现示例。
部署运行你感兴趣的模型镜像

JNIJava Native Interface)是Java语言提供的一种机制,用于在Java应用程序中调用本地代码(Native Code)或者让本地代码调用Java代码。它为Java和其他编程语言(如CC++)之间的交互提供了一种标准的接口。

使用JNI,可以通过以下步骤实现Java和本地代码之间的交互:

  • 定义Native方法:在Java类中声明一个native修饰符的方法,表示该方法是一个本地方法,实现将在本地代码中定义。
  • 生成包含本地方法的头文件:使用Javac命令编译包含Native方法的Java源文件,然后使用javah命令生成头文件(.h文件),该头文件将包含本地方法的声明。
  • 实现本地代码:在C或C++源文件中编写与Java中声明的Native方法对应的函数,可以使用JNI提供的API来访问Java对象、调用Java方法等。
  • 编译本地代码:编译包含本地代码的源文件,并生成共享库(动态链接库或静态库)。
  • 在Java中加载本地库:使用System.loadLibrary()方法或System.load()方法在Java中加载本地库。这将使得本地代码可在Java环境中被访问和调用。
  • 调用本地方法:在Java中通过调用Native方法来调用本地代码。本地代码将执行与该方法对应的本地实现,并返回结果给Java。

JNI提供了一系列的API来支持Java和本地代码之间的交互,包括访问Java对象、调用Java方法、异常处理等。它还提供了一些功能,如线程绑定、字符串转换、数组访问等。

使用JNI可以充分利用Java的跨平台性和易用性,并在需要使用本地代码的场景下提供灵活性和性能优势。

数据类型

在JNI中,有一系列的数据类型用于表示Java和本地代码之间的数据交互。这些数据类型对应了Java的数据类型,并提供了在Java和本地代码之间进行数据转换的功能。

以下是一些常用的JNI数据类型:

基本数据类型:

jboolean

对应Java的boolean类型。

jbyte

对应Java的byte类型。

jchar

对应Java的char类型。

jshort

对应Java的short类型。

jint

对应Java的int类型。

jlong

对应Java的long类型。

jfloat

对应Java的float类型。

jdouble

对应Java的double类型。

引用类型:

jobject

对应Java的任意对象类型。

jclass

对应Java的Class类型。

jstring

对应Java的String类型。

jarray

对应Java的数组类型。

jthrowable

对应Java的Throwable类型。

其他类型:

jbooleanArray

对应Java的boolean[]类型。

jbyteArray

对应Java的byte[]类型。

jcharArray

对应Java的char[]类型。

jshortArray

对应Java的short[]类型。

jintArray

对应Java的int[]类型。

jlongArray

对应Java的long[]类型。

jfloatArray

对应Java的float[]类型。

jdoubleArray

对应Java的double[]类型。

在JNI中,还有一些用于表示方法签名、字段描述符等的数据类型,如jmethodID、jfieldID等。

这些JNI数据类型可以在Java和本地代码之间进行数据转换,使得我们可以在Java代码和本地代码之间传递参数、返回结果,以及访问对象的属性和方法。

基本方法

在JNI中,有几种方法用于在Java和本地代码之间进行调用和交互。以下是一些常用的JNI方法:

JNIEnv结构体中的方法:

GetMethodID:

通过方法名和方法签名获取Java类中的方法ID。

Call<Type>Method:

调用Java对象的实例方法,并返回相应类型的结果。

CallStatic<Type>Method:

调用Java类的静态方法,并返回相应类型的结果。

NewObject:

创建Java类的新对象,并调用其构造函数初始化。

Get<Type>Field:

获取Java对象字段的值。

Set<Type>Field:

设置Java对象字段的值。

JNIEnv结构体中的数组方法:

New<Type>Array:

创建Java数组对象。

Get<Type>ArrayElements:

获取Java数组的元素。

Release<Type>ArrayElements:

释放Java数组的元素。

GetArrayLength:

获取Java数组的长度。

字符串相关方法:

NewStringUTF:

从C字符串创建Java字符串对象。

GetStringUTFChars:

将Java字符串转换为C字符串。

ReleaseStringUTFChars:

释放通过GetStringUTFChars获取的C字符串。

异常处理方法:

ExceptionCheck:

检查是否有异常发生。

ExceptionOccurred:

获取当前抛出的异常对象。

ExceptionDescribe:

打印异常堆栈信息。

ExceptionClear:

清除当前的异常状态。

上述方法只是JNI提供的一部分方法,用于实现Java和本地代码之间的交互。通过这些方法,可以在Java中调用本地代码的函数,也可以在本地代码中调用Java对象的方法。

需要注意的是,在使用JNI调用Java方法时,需要根据方法的签名(包括方法名、入参类型和返回值类型)正确地进行匹配。另外,还需要注意内存管理和异常处理,以确保程序的正确性和稳定性。

签名

在JNI中,方法签名用来表示Java方法的参数类型和返回值类型。JNI使用一种特定的格式来表示方法签名,以便在Java和本地代码之间进行正确的类型匹配。

方法签名的基本格式如下:

(参数类型1, 参数类型2, ...)返回值类型

其中,参数类型和返回值类型使用特定的字母缩写表示。以下是常见的类型缩写:

Z

boolean类型

B

byte类型

C

char类型

S

short类型

I

int类型

J

long类型

F

float类型

D

double类型

V

void类型

L<类名>;

引用类型,例如Ljava/lang/String;表示String类型

对于数组类型,可以使用[来表示数组,例如[I表示int[]类型。

以下是几个示例方法签名的示例:

  1. public int add(int a, int b) 的签名为 (II)I
  2. public void printMessage(String message) 的签名为 (Ljava/lang/String;)V
  3. public static native double calculateAverage(int[] numbers) 的签名为 ([I)D

可以使用工具或者手动将Java方法的参数类型和返回值类型转换为对应的JNI签名。

在JNI中,使用方法签名来获取方法ID、调用方法等操作,以确保Java和本地代码之间的正确交互。

JNIEnv

JNIEnv(Java Native Interface Environment)是Java Native Interface(JNI)提供的一个关键结构体,用于在Java代码和本地代码之间进行交互和通信。JNIEnv提供了一组函数指针,用于调用Java对象的方法、访问字段、操作数组等。

JNIEnv结构体定义如下:

typedef const struct JNINativeInterface *JNIEnv;

JNIEnv结构体中包含了一系列函数指针,这些函数指针定义在JNINativeInterface结构体中,可以通过JNIEnv结构体进行调用。具体的函数指针包括以下一些常见的函数:

GetMethodID

通过方法名和方法签名获取Java类中方法的ID。

Call<Type>Method

调用Java对象的实例方法,并返回相应类型的结果。

CallStatic<Type>Method

调用Java类的静态方法,并返回相应类型的结果。

NewObject

创建Java类的新对象,并调用其构造函数初始化。

Get<Type>Field

获取Java对象字段的值。

Set<Type>Field

设置Java对象字段的值。

New<Type>Array

创建Java数组对象。

Get<Type>ArrayElements

获取Java数组的元素。

Release<Type>ArrayElements

释放Java数组的元素。

除了上述函数指针,JNIEnv结构体还包含了其他一些函数指针,用于异常处理、类和对象操作等。

在JNI中,JNIEnv结构体是在本地代码中使用的重要接口,它提供了许多函数指针,用于与Java代码进行交互和通信。通过JNIEnv,本地代码可以调用Java对象的方法、访问字段、创建对象等操作。

举个例子

JNI实现

#include <jni.h>
#include <string>

#include <utils/Log.h>

#define LOG_TAG "CSKMicArrayControl-JNI"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, 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__)

extern "C" {

#include "swcskaudio.h"

JNIEXPORT jint JNICALL
Java_com_example_media_CSKMicArrayControl_native_1AudioRecord_1setup(
    JNIEnv *env,
    jobject thiz,
    jint audioFormat,
    jint channels,
    jint rate) {
    // TODO: implement native_AudioRecord_setup()
    jint result;
    result = sw_csk_audio_open(channels, rate, audioFormat);
    if (result < 0) {
        LOGE("sw_csk_audio_open filed!");
        return -1;
    }

    return result;
}

struct audiorecord_callback_cookie {
    JNIEnv *env;
    jclass clazz = nullptr;
    jmethodID methodID;
    bool busy;
};

static audiorecord_callback_cookie callbackInfo;

// typedef void (*send_record_data)(char *, unsigned int);
static void sendRecordDataCallback(char *recordData, int length) {

    // Creating Arrays in Java Objects
    jbyteArray array = callbackInfo.env->NewByteArray(length);
    jbyte* buf = callbackInfo.env->GetByteArrayElements(array, NULL);

    // copy ByteArray data to Java Objects
    callbackInfo.env->SetByteArrayRegion(array, 0, length,
                                         reinterpret_cast<jbyte *>(recordData));

    callbackInfo.env->CallStaticVoidMethod(callbackInfo.clazz,
                                           callbackInfo.methodID, array, length);

    callbackInfo.env->ReleaseByteArrayElements(array, buf, JNI_ABORT);
    callbackInfo.env->DeleteLocalRef(array);
}

JNIEXPORT jint JNICALL
Java_com_example_media_CSKMicArrayControl_native_1AudioRecord_1start(
    JNIEnv *env,
    jobject thiz,
    jint mode) {
    // TODO: implement native_AudioRecord_start()
    if (1 == mode) {
        callbackInfo.env = env;
        callbackInfo.clazz = env->GetObjectClass(thiz);
        callbackInfo.methodID = env->GetStaticMethodID(callbackInfo.clazz,
                                "receiveRecordData",
                                "([BI)V");

        callbackInfo.busy = false;

        sw_start_audio_record_th(sendRecordDataCallback);
    }
    return 0;
}

JNIEXPORT jint JNICALL
Java_com_example_media_CSKMicArrayControl_native_1AudioRecord_1stop(
    JNIEnv *env,
    jobject thiz) {
    // TODO: implement native_AudioRecord_stop()
    return 0;
}

JNIEXPORT jint JNICALL
Java_com_example_media_CSKMicArrayControl_native_1AudioRecord_1release(
    JNIEnv *env,
    jobject thiz) {
    // TODO: implement native_AudioRecord_release()
    sw_csk_audio_close();
    return 0;
}

}

Java 调用

package com.example.media;

import android.util.Log;

public class CSKMicArrayControl {

	private static final String TAG = "CSKMicArrayControl";	

	static {
        System.loadLibrary("CSKMicArrayControl");
    }

	public native static String native_getMicroModel();

	public native static String native_getMicroVersion();

	public native int native_getMicroStatus();

	public native int native_getPattern();

	public native int native_AudioRecord_setup(int audioFormat, int channels, int rate);

	public native int native_AudioRecord_start(int mode);

	public native int native_AudioRecord_stop();

	public native int native_AudioRecord_release();

	public native int native_AudioRecord_readInArray(byte[] audioData, int sizeInBytes, int readMode);

	public static void onWakeUp(String mWakeUpMsg) {
		
		Log.i(TAG, "DCXLOG ==> onWakeUp");	
	}

	public static void onEventMessage(String mEventMsg) {

	    Log.i(TAG, "DCXLOG ==> onEventMessage");
	}

	public static void receiveRecordData(byte[] audioData, int sizeInBytes) {

	    Log.i(TAG, "DCXLOG ==> receiveRecordData" + audioData.toString() + "len:" + sizeInBytes);		
	}
}

以上例子种实现了一个将音频数据往应用层上抛的逻辑,通过回调方式;

您可能感兴趣的与本文相关的镜像

ACE-Step

ACE-Step

音乐合成
ACE-Step

ACE-Step是由中国团队阶跃星辰(StepFun)与ACE Studio联手打造的开源音乐生成模型。 它拥有3.5B参数量,支持快速高质量生成、强可控性和易于拓展的特点。 最厉害的是,它可以生成多种语言的歌曲,包括但不限于中文、英文、日文等19种语言

### JNI使用方法 JNIJava Native Interface)是一种用于让Java代码与其他编程语言(如C、C++)编写的代码进行交互的技术。以下是关于如何使用JNI实现Java与本地代码交互的具体说明。 #### 定义本地方法 在Java中,可以通过`native`关键字来声明一个本地方法。该方法的实际实现将在本地代码中完成。例如: ```java public class TestLibrary { public native void testPrint(); static { System.loadLibrary("testlibrary"); // 加载本地库 } public static void main(String[] args) { new TestLibrary().testPrint(); // 调用本地方法 } } ``` 上述代码中的`System.loadLibrary()`方法会加载指定名称的本地库文件[^1]。 #### 生成头文件 为了使本地代码能够识别Java类及其方法签名,需要通过`javah`命令生成对应的头文件。假设上面的Java类名为`TestLibrary`,则执行如下命令可生成`.h`文件: ```bash javac TestLibrary.java javah -jni TestLibrary ``` 这将生成一个名为`TestLibrary.h`的头文件,其中包含了供本地代码使用的函数原型[^4]。 #### 编写并实现本地代码 接下来,在C或C++中编写具体的功能逻辑,并将其链接到之前生成的头文件上。下面是一个简单的例子展示如何实现`testPrint`方法: ```cpp #include "com_baidu_xxx_jni_TestLibrary.h" #include <jni.h> #include <iostream> // 实现 sayHello 本地方法 JNIEXPORT void JNICALL Java_com_baidu_xxx_jni_TestLibrary_testPrint(JNIEnv *env, jobject obj) { std::cout << "Print Code C++!" << std::endl; } ``` 此部分实现了由Java端调用的打印操作。 #### 构建共享库 最后一步是构建包含所实现功能的动态库。对于不同平台来说,最终产物可能是`.dll`(Windows), `.so`(Linux/Unix)或者`.dylib`(macOS)。通常情况下,可以借助像Makefile这样的工具自动化这一过程。 --- ### 注意事项 - **性能考虑**:虽然JNI提供了强大的跨语言能力,但它也可能带来额外开销。因此只应在必要时才采用这种方式。 - **异常处理**:当从原生层抛出错误时,应妥善管理这些情况以免影响整个应用程序稳定性[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

你好,工程师

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

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

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

打赏作者

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

抵扣说明:

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

余额充值