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

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

你好,工程师

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

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

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

打赏作者

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

抵扣说明:

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

余额充值