JNI案例

wen java案例 3

本文目录导读:

JNI案例

  1. 项目结构
  2. 步骤1:编写Java类
  3. 步骤2:生成C头文件
  4. 步骤3:实现C代码
  5. 步骤4:编译动态库
  6. 步骤5:运行程序
  7. 完整构建脚本(build.sh)
  8. 输出结果
  9. 关键概念解释

我来给您一个完整的JNI(Java Native Interface)案例,从Java调用C/C++代码。

项目结构

JNIDemo/
├── src/
│   └── com/example/
│       └── NativeDemo.java
├── native/
│   ├── native_demo.c
│   └── native_demo.h
└── lib/

步骤1:编写Java类

// NativeDemo.java
package com.example;
public class NativeDemo {
    // 声明native方法
    public native int add(int a, int b);
    public native String greet(String name);
    public native int[] sortArray(int[] array);
    // 加载动态库
    static {
        System.loadLibrary("nativedemo");
    }
    public static void main(String[] args) {
        NativeDemo demo = new NativeDemo();
        // 测试加法
        int sum = demo.add(10, 20);
        System.out.println("10 + 20 = " + sum);
        // 测试字符串处理
        String greeting = demo.greet("小明");
        System.out.println("Greeting: " + greeting);
        // 测试数组排序
        int[] array = {5, 3, 8, 1, 9, 2};
        int[] sortedArray = demo.sortArray(array);
        System.out.print("Sorted array: ");
        for (int num : sortedArray) {
            System.out.print(num + " ");
        }
        System.out.println();
    }
}

步骤2:生成C头文件

编译Java文件并生成头文件:

# 编译Java文件
javac src/com/example/NativeDemo.java
# 生成头文件
javah -jni -classpath src com.example.NativeDemo

生成的native_demo.h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_NativeDemo */
#ifndef _Included_com_example_NativeDemo
#define _Included_com_example_NativeDemo
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_NativeDemo
 * Method:    add
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_com_example_NativeDemo_add
  (JNIEnv *, jobject, jint, jint);
/*
 * Class:     com_example_NativeDemo
 * Method:    greet
 * Signature: (Ljava/lang/String;)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_example_NativeDemo_greet
  (JNIEnv *, jobject, jstring);
/*
 * Class:     com_example_NativeDemo
 * Method:    sortArray
 * Signature: ([I)[I
 */
JNIEXPORT jintArray JNICALL Java_com_example_NativeDemo_sortArray
  (JNIEnv *, jobject, jintArray);
#ifdef __cplusplus
}
#endif
#endif

步骤3:实现C代码

// native_demo.c
#include <jni.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "native_demo.h"
// 实现加法
JNIEXPORT jint JNICALL Java_com_example_NativeDemo_add
  (JNIEnv *env, jobject obj, jint a, jint b) {
    printf("C: Adding %d + %d\n", a, b);
    return a + b;
}
// 实现字符串处理
JNIEXPORT jstring JNICALL Java_com_example_NativeDemo_greet
  (JNIEnv *env, jobject obj, jstring name) {
    // 将Java字符串转换为C字符串
    const char *nativeName = (*env)->GetStringUTFChars(env, name, NULL);
    if (nativeName == NULL) {
        return NULL; // 内存分配失败
    }
    printf("C: Greeting %s\n", nativeName);
    // 创建新的字符串
    char greeting[100];
    snprintf(greeting, sizeof(greeting), "你好,%s!来自C的问候!", nativeName);
    // 释放原始字符串
    (*env)->ReleaseStringUTFChars(env, name, nativeName);
    // 将C字符串转换为Java字符串并返回
    return (*env)->NewStringUTF(env, greeting);
}
// 实现数组排序
JNIEXPORT jintArray JNICALL Java_com_example_NativeDemo_sortArray
  (JNIEnv *env, jobject obj, jintArray array) {
    // 获取数组长度
    jsize length = (*env)->GetArrayLength(env, array);
    // 获取数组元素
    jint *elements = (*env)->GetIntArrayElements(env, array, NULL);
    if (elements == NULL) {
        return NULL; // 内存分配失败
    }
    printf("C: Sorting array of length %d\n", length);
    // 打印原始数组
    printf("Original array: ");
    for (int i = 0; i < length; i++) {
        printf("%d ", elements[i]);
    }
    printf("\n");
    // 使用冒泡排序
    for (int i = 0; i < length - 1; i++) {
        for (int j = 0; j < length - 1 - i; j++) {
            if (elements[j] > elements[j + 1]) {
                int temp = elements[j];
                elements[j] = elements[j + 1];
                elements[j + 1] = temp;
            }
        }
    }
    // 创建新的Java数组
    jintArray result = (*env)->NewIntArray(env, length);
    if (result == NULL) {
        (*env)->ReleaseIntArrayElements(env, array, elements, 0);
        return NULL;
    }
    // 将排序后的数据设置到新数组
    (*env)->SetIntArrayRegion(env, result, 0, length, elements);
    // 释放原始数组
    (*env)->ReleaseIntArrayElements(env, array, elements, 0);
    return result;
}

步骤4:编译动态库

Linux/Mac系统:

# 编译C代码为动态库
gcc -shared -fPIC -I$JAVA_HOME/include -I$JAVA_HOME/include/linux \
    -o lib/libnativedemo.so native/native_demo.c

Windows系统:

# 使用MinGW或Visual Studio
gcc -shared -I"%JAVA_HOME%\include" -I"%JAVA_HOME%\include\win32" \
    -o lib\nativedemo.dll native\native_demo.c

Mac OS X系统:

gcc -shared -fPIC -I$JAVA_HOME/include -I$JAVA_HOME/include/darwin \
    -o lib/libnativedemo.dylib native/native_demo.c

步骤5:运行程序

# 指定库路径并运行
java -Djava.library.path=lib -classpath src com.example.NativeDemo

完整构建脚本(build.sh)

#!/bin/bash
# 设置JAVA_HOME(根据自己的环境修改)
export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
# 清理
rm -rf src/com/example/*.class
rm -rf lib/*.so lib/*.dylib
# 编译Java
echo "Compiling Java..."
javac src/com/example/NativeDemo.java
# 生成头文件
echo "Generating header file..."
javah -jni -classpath src com.example.NativeDemo
mv com_example_NativeDemo.h native/
# 编译C动态库
echo "Compiling C library..."
gcc -shared -fPIC -I$JAVA_HOME/include -I$JAVA_HOME/include/linux \
    -o lib/libnativedemo.so native/native_demo.c
echo "Build complete!"
# 运行测试
echo "Running test..."
java -Djava.library.path=lib -classpath src com.example.NativeDemo

输出结果

C: Adding 10 + 20
10 + 20 = 30
C: Greeting 小明
Greeting: 你好,小明!来自C的问候!
C: Sorting array of length 6
Original array: 5 3 8 1 9 2 
Sorted array: 1 2 3 5 8 9

关键概念解释

  1. JNIEnv:代表JNI环境的指针,用于调用JNI函数

  2. jobject:表示Java对象的引用

  3. 类型映射

    • jint ↔ Java int
    • jstring ↔ Java String
    • jintArray ↔ Java int[]
  4. JNI函数

    • GetStringUTFChars():获取Java字符串的C表示
    • NewStringUTF():创建Java字符串
    • GetIntArrayElements():获取Java数组的C表示
    • ReleaseStringUTFChars():释放字符串资源

这个案例涵盖了JNI中最常见的操作:基本类型传递、字符串处理和数组操作。

抱歉,评论功能暂时关闭!