第14章:JNI和NDK编程

章节定位:本章介绍Android Native开发技术,包括JNI调用、NDK开发、Java与C/C++互调等核心知识。


14.1 JNI概述

14.1.1 什么是JNI

JNI(Java Native Interface):Java本地接口

  • Java调用C/C++代码的桥梁
  • 允许Java代码与本地应用或库交互

为什么需要JNI?

场景 原因
性能敏感 C/C++比Java快(图像处理、音视频编解码)
复用已有代码 使用现有的C/C++库(FFmpeg、OpenCV)
硬件访问 直接操作硬件(摄像头、传感器底层驱动)
代码保护 C/C++代码反编译难度大
跨平台库 同一套C/C++代码在Android和iOS使用

14.1.2 JNI vs NDK

概念 定义 作用
JNI Java Native Interface Java与C/C++互调的规范
NDK Native Development Kit Android官方提供的Native开发工具集

关系:NDK是实现JNI的工具集,JNI是规范。


14.2 NDK开发环境搭建

14.2.1 安装NDK

方式1:Android Studio安装

  1. 打开 SDK Manager
  2. SDK Tools → 勾选 NDK (Side by side)、CMake
  3. 点击 Apply 下载安装

方式2:手动下载

# 下载NDK
https://developer.android.com/ndk/downloads

# 配置环境变量(macOS/Linux)
export NDK_HOME=/path/to/ndk
export PATH=$PATH:$NDK_HOME

14.2.2 配置项目

在build.gradle中启用NDK

android {
    defaultConfig {
        ndk {
            // 指定支持的CPU架构
            abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
        }
    }

    externalNativeBuild {
        cmake {
            path "src/main/cpp/CMakeLists.txt" // CMake配置文件路径
            version "3.18.1"
        }
    }
}

创建CMakeLists.txt(C/C++构建配置):

# 指定CMake最低版本
cmake_minimum_required(VERSION 3.18.1)

# 项目名称
project("native-lib")

# 添加库
add_library(
    native-lib        # 库名称
    SHARED            # 动态库(SHARED)或静态库(STATIC)
    native-lib.cpp    # C++源文件
)

# 查找Android日志库
find_library(
    log-lib
    log
)

# 链接库
target_link_libraries(
    native-lib
    ${log-lib}
)

14.3 JNI开发流程

14.3.1 完整开发步骤

Step 1:在Java中声明native方法

public class JNIHelper {
    // 加载native库
    static {
        System.loadLibrary("native-lib");
    }

    // 声明native方法(无方法体)
    public native String stringFromJNI();
    public native int add(int a, int b);
    public native void callJavaMethod();
}

Step 2:生成C/C++头文件(可选)

# 旧方法(javah,已废弃)
javah -jni -classpath . -d jni com.example.app.JNIHelper

# 新方法(Android Studio自动生成)
# 或手动编写,遵循JNI命名规范

Step 3:实现C/C++函数

#include <jni.h>
#include <string>
#include <android/log.h>

// JNI函数命名规范:Java_{包名}_{类名}_{方法名}
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_app_JNIHelper_stringFromJNI(
    JNIEnv* env,
    jobject /* this */) {

    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

extern "C" JNIEXPORT jint JNICALL
Java_com_example_app_JNIHelper_add(
    JNIEnv* env,
    jobject /* this */,
    jint a,
    jint b) {

    __android_log_print(ANDROID_LOG_DEBUG, "JNI", "add called: %d + %d", a, b);
    return a + b;
}

Step 4:编译生成.so库

# Android Studio会自动编译,生成:
# app/build/intermediates/cmake/debug/obj/
#   ├── armeabi-v7a/libnative-lib.so
#   ├── arm64-v8a/libnative-lib.so
#   ├── x86/libnative-lib.so
#   └── x86_64/libnative-lib.so

Step 5:在Java中调用

JNIHelper helper = new JNIHelper();
String result = helper.stringFromJNI(); // "Hello from C++"
int sum = helper.add(10, 20);           // 30

14.3.2 JNI函数命名规范

格式Java_{package}_{class}_{method}

示例

Java类:com.example.app.JNIHelper
方法名:stringFromJNI

JNI函数名:
Java_com_example_app_JNIHelper_stringFromJNI

特殊字符处理

  • 下划线_替换为_1
  • 分号;替换为_2
  • 左中括号[替换为_3

示例

Java方法:my_method
JNI函数:Java_com_example_app_JNIHelper_my_1method

14.4 JNI数据类型

14.4.1 基本数据类型映射

Java类型 JNI类型 C/C++类型 字节数
boolean jboolean unsigned char 1
byte jbyte signed char 1
char jchar unsigned short 2
short jshort short 2
int jint int 4
long jlong long long 8
float jfloat float 4
double jdouble double 8
void void void -

示例

// Java: public native int add(int a, int b);
extern "C" JNIEXPORT jint JNICALL
Java_com_example_JNIHelper_add(JNIEnv* env, jobject thiz, jint a, jint b) {
    return a + b;
}

14.4.2 引用数据类型

Java类型 JNI类型 说明
Object jobject Java对象
Class jclass Java类
String jstring Java字符串
Throwable jthrowable Java异常
Object[] jobjectArray 对象数组
boolean[] jbooleanArray boolean数组
int[] jintArray int数组
其他数组类型

重要:引用类型都是指针,操作需要通过JNIEnv的方法。


14.5 JNIEnv详解

14.5.1 JNIEnv是什么

定义

  • JNI环境指针,指向一个函数表
  • 提供了200+个JNI函数
  • 每个线程独立的JNIEnv

获取方式

// 方式1:作为参数传入(最常用)
JNIEXPORT void JNICALL
Java_com_example_JNIHelper_nativeMethod(JNIEnv* env, jobject thiz) {
    // env已经可以直接使用
}

// 方式2:从JavaVM获取
JavaVM* jvm;
JNIEnv* env;
jvm->AttachCurrentThread(&env, NULL);

14.5.2 常用JNIEnv方法

1. 字符串操作
// Java String → C字符串
jstring jstr = ... ; // Java传入的String
const char* cstr = env->GetStringUTFChars(jstr, NULL);
printf("String: %s\n", cstr);
env->ReleaseStringUTFChars(jstr, cstr); // ⚠️ 必须释放

// C字符串 → Java String
const char* cstr = "Hello";
jstring jstr = env->NewStringUTF(cstr);
return jstr;

⚠️ 内存管理

  • GetStringUTFChars会分配内存,必须调用ReleaseStringUTFChars释放
  • 否则会导致内存泄漏
2. 数组操作
// Java int[] → C数组
jintArray jarray = ... ; // Java传入的int[]
jint* carray = env->GetIntArrayElements(jarray, NULL);
jsize length = env->GetArrayLength(jarray);

for (int i = 0; i < length; i++) {
    carray[i] *= 2; // 修改数组元素
}

env->ReleaseIntArrayElements(jarray, carray, 0); // 同步修改回Java

// C数组 → Java int[]
jint carr[] = {1, 2, 3, 4, 5};
jintArray jarray = env->NewIntArray(5);
env->SetIntArrayRegion(jarray, 0, 5, carr);
return jarray;
3. 调用Java方法
// 调用Java对象的方法
jclass clazz = env->GetObjectClass(thiz); // 获取类

// 获取方法ID(方法签名见下节)
jmethodID mid = env->GetMethodID(clazz, "javaMethod", "(I)V");

// 调用方法
env->CallVoidMethod(thiz, mid, 123);
4. 访问Java字段
// 获取字段ID
jfieldID fid = env->GetFieldID(clazz, "count", "I");

// 读取字段
jint count = env->GetIntField(thiz, fid);

// 修改字段
env->SetIntField(thiz, fid, count + 1);
5. 创建Java对象
// 获取类
jclass clazz = env->FindClass("java/lang/String");

// 获取构造方法
jmethodID constructor = env->GetMethodID(clazz, "<init>", "(Ljava/lang/String;)V");

// 创建对象
jstring param = env->NewStringUTF("Hello");
jobject obj = env->NewObject(clazz, constructor, param);

14.6 JNI方法签名

14.6.1 方法签名规则

格式(参数类型)返回值类型

基本类型签名

Java类型 签名
boolean Z
byte B
char C
short S
int I
long J
float F
double D
void V

引用类型签名

Java类型 签名
Object Ljava/lang/Object;
String Ljava/lang/String;
int[] [I
String[] [Ljava/lang/String;

14.6.2 方法签名示例

示例1:无参无返回

public void method()

签名:()V

示例2:int参数,String返回

public String method(int a)

签名:(I)Ljava/lang/String;

示例3:多个参数

public int method(String str, int num, boolean flag)

签名:(Ljava/lang/String;IZ)I

示例4:数组参数

public void method(int[] array)

签名:([I)V

14.6.3 自动生成签名

方式1:javap命令

# 编译Java类
javac JNIHelper.java

# 查看方法签名
javap -s JNIHelper

# 输出示例
public native java.lang.String stringFromJNI();
  descriptor: ()Ljava/lang/String;

public native int add(int, int);
  descriptor: (II)I

方式2:在线工具

  • http://www.javadecompilers.com/jni

14.7 JNI调用Java方法

14.7.1 调用实例方法

// Java类
public class JNIHelper {
    public void javaMethod(int value) {
        Log.d("JNI", "javaMethod called: " + value);
    }

    public native void callJavaMethod();
}
// C++实现
extern "C" JNIEXPORT void JNICALL
Java_com_example_JNIHelper_callJavaMethod(JNIEnv* env, jobject thiz) {
    // 1. 获取类
    jclass clazz = env->GetObjectClass(thiz);

    // 2. 获取方法ID
    jmethodID mid = env->GetMethodID(clazz, "javaMethod", "(I)V");

    if (mid == NULL) {
        __android_log_print(ANDROID_LOG_ERROR, "JNI", "Method not found");
        return;
    }

    // 3. 调用方法
    env->CallVoidMethod(thiz, mid, 123);

    // 4. 检查异常
    if (env->ExceptionCheck()) {
        env->ExceptionDescribe();
        env->ExceptionClear();
    }
}

14.7.2 调用静态方法

public class JNIHelper {
    public static void staticMethod(String str) {
        Log.d("JNI", "staticMethod: " + str);
    }
}
extern "C" JNIEXPORT void JNICALL
Java_com_example_JNIHelper_callStaticMethod(JNIEnv* env, jobject thiz) {
    // 1. 获取类(使用FindClass)
    jclass clazz = env->FindClass("com/example/JNIHelper");

    // 2. 获取静态方法ID
    jmethodID mid = env->GetStaticMethodID(clazz, "staticMethod", "(Ljava/lang/String;)V");

    // 3. 调用静态方法
    jstring param = env->NewStringUTF("Hello from C++");
    env->CallStaticVoidMethod(clazz, mid, param);
}

14.7.3 调用父类方法

// 调用父类的方法(非虚方法调用)
jclass superClazz = env->GetSuperclass(clazz);
jmethodID mid = env->GetMethodID(superClazz, "superMethod", "()V");
env->CallNonvirtualVoidMethod(thiz, superClazz, mid);

14.8 JNI引用类型

14.8.1 三种引用类型

引用类型 生命周期 使用场景
局部引用(Local Reference) 函数返回后自动释放 大部分情况
全局引用(Global Reference) 手动释放 跨函数、跨线程使用
弱全局引用(Weak Global Reference) 不阻止GC 缓存Java对象

14.8.2 局部引用

特点

  • JNI函数默认返回局部引用
  • 函数返回后自动释放
  • 不能跨函数、跨线程使用

示例

JNIEXPORT void JNICALL
Java_com_example_JNIHelper_localRefDemo(JNIEnv* env, jobject thiz) {
    jstring str = env->NewStringUTF("Hello"); // 局部引用
    // 函数结束,str自动释放

    // 手动释放(可选)
    env->DeleteLocalRef(str);
}

⚠️ 局部引用表溢出

// 错误示例:循环创建大量局部引用
for (int i = 0; i < 100000; i++) {
    jstring str = env->NewStringUTF("Hello"); // ❌ 局部引用表溢出
    // 未释放
}

// 正确做法
for (int i = 0; i < 100000; i++) {
    jstring str = env->NewStringUTF("Hello");
    // ... 使用str
    env->DeleteLocalRef(str); // ✅ 手动释放
}

14.8.3 全局引用

使用场景

  • 在多个函数间共享Java对象
  • 在C++成员变量中保存Java对象
  • 在子线程中使用Java对象

示例

jclass g_clazz = NULL; // 全局变量

JNIEXPORT void JNICALL
Java_com_example_JNIHelper_init(JNIEnv* env, jobject thiz) {
    // 创建全局引用
    jclass localClazz = env->FindClass("com/example/MyClass");
    g_clazz = (jclass)env->NewGlobalRef(localClazz);

    // 释放局部引用
    env->DeleteLocalRef(localClazz);
}

JNIEXPORT void JNICALL
Java_com_example_JNIHelper_destroy(JNIEnv* env, jobject thiz) {
    // 释放全局引用
    if (g_clazz != NULL) {
        env->DeleteGlobalRef(g_clazz);
        g_clazz = NULL;
    }
}

JNIEXPORT void JNICALL
Java_com_example_JNIHelper_useGlobalRef(JNIEnv* env, jobject thiz) {
    // 使用全局引用
    jmethodID mid = env->GetMethodID(g_clazz, "method", "()V");
    // ...
}

14.8.4 弱全局引用

特点

  • 不阻止Java对象被GC回收
  • 使用前需检查是否已被回收

示例

jweak g_weakRef = NULL;

JNIEXPORT void JNICALL
Java_com_example_JNIHelper_setWeakRef(JNIEnv* env, jobject thiz, jobject obj) {
    // 创建弱全局引用
    g_weakRef = env->NewWeakGlobalRef(obj);
}

JNIEXPORT void JNICALL
Java_com_example_JNIHelper_useWeakRef(JNIEnv* env, jobject thiz) {
    // 检查对象是否被回收
    if (env->IsSameObject(g_weakRef, NULL)) {
        __android_log_print(ANDROID_LOG_WARN, "JNI", "Object has been GCed");
        return;
    }

    // 使用弱引用
    // ...
}

JNIEXPORT void JNICALL
Java_com_example_JNIHelper_destroyWeakRef(JNIEnv* env, jobject thiz) {
    if (g_weakRef != NULL) {
        env->DeleteWeakGlobalRef(g_weakRef);
        g_weakRef = NULL;
    }
}

14.9 JNI异常处理

14.9.1 检查异常

JNIEXPORT void JNICALL
Java_com_example_JNIHelper_exceptionDemo(JNIEnv* env, jobject thiz) {
    jclass clazz = env->FindClass("com/example/NonExistClass");

    // 方式1:检查是否有异常
    if (env->ExceptionCheck()) {
        env->ExceptionDescribe(); // 打印异常堆栈到logcat
        env->ExceptionClear();     // 清除异常
        return;
    }

    // 方式2:检查返回值
    if (clazz == NULL) {
        // 发生了异常
        env->ExceptionClear();
        return;
    }
}

14.9.2 抛出异常到Java

JNIEXPORT void JNICALL
Java_com_example_JNIHelper_throwException(JNIEnv* env, jobject thiz) {
    // 方式1:抛出指定异常类
    jclass exceptionClazz = env->FindClass("java/lang/IllegalArgumentException");
    env->ThrowNew(exceptionClazz, "Invalid argument from native");

    // 方式2:抛出已存在的异常对象
    jthrowable exception = ... ; // 获取异常对象
    env->Throw(exception);
}

Java端捕获

try {
    jniHelper.throwException();
} catch (IllegalArgumentException e) {
    Log.e("JNI", "Native exception: " + e.getMessage());
}

14.10 JNI线程操作

14.10.1 在Native线程中调用Java

问题

  • JNIEnv是线程局部的
  • 子线程没有JNIEnv

解决方案

JavaVM* g_jvm = NULL; // 全局保存JavaVM

JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
    g_jvm = vm; // 保存JavaVM
    return JNI_VERSION_1_6;
}

void* thread_function(void* arg) {
    JNIEnv* env;

    // 将当前线程附加到JavaVM
    int status = g_jvm->AttachCurrentThread(&env, NULL);
    if (status != JNI_OK) {
        return NULL;
    }

    // 现在可以使用env调用Java方法
    jclass clazz = env->FindClass("com/example/JNIHelper");
    jmethodID mid = env->GetStaticMethodID(clazz, "callback", "()V");
    env->CallStaticVoidMethod(clazz, mid);

    // 分离线程
    g_jvm->DetachCurrentThread();

    return NULL;
}

JNIEXPORT void JNICALL
Java_com_example_JNIHelper_startNativeThread(JNIEnv* env, jobject thiz) {
    pthread_t thread;
    pthread_create(&thread, NULL, thread_function, NULL);
    pthread_detach(thread);
}

14.10.2 JNI_OnLoad和JNI_OnUnload

JNI_OnLoad

  • 动态库加载时调用(System.loadLibrary()
  • 用于初始化(保存JavaVM、注册Native方法)

JNI_OnUnload

  • 动态库卸载时调用
  • 用于清理资源
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
    __android_log_print(ANDROID_LOG_INFO, "JNI", "JNI_OnLoad called");

    g_jvm = vm;

    // 获取JNIEnv
    JNIEnv* env;
    if (vm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK) {
        return JNI_ERR;
    }

    // 动态注册Native方法(可选)
    // registerNativeMethods(env);

    return JNI_VERSION_1_6;
}

JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved) {
    __android_log_print(ANDROID_LOG_INFO, "JNI", "JNI_OnUnload called");

    // 清理全局引用等资源
    JNIEnv* env;
    vm->GetEnv((void**)&env, JNI_VERSION_1_6);

    if (g_clazz != NULL) {
        env->DeleteGlobalRef(g_clazz);
        g_clazz = NULL;
    }
}

14.11 JNI性能优化

14.11.1 减少JNI调用次数

问题
JNI调用有开销(参数转换、类型检查)

优化

// ❌ 不好:频繁调用JNI
for (int i = 0; i < 1000; i++) {
    nativeProcess(i); // 调用1000次
}

// ✅ 好:批量处理
int[] data = new int[1000];
for (int i = 0; i < 1000; i++) {
    data[i] = i;
}
nativeProcessBatch(data); // 只调用1次

14.11.2 缓存jclass和jmethodID

问题
每次查找类和方法ID都有开销

优化

// ❌ 不好:每次都查找
JNIEXPORT void JNICALL
Java_com_example_JNIHelper_method(JNIEnv* env, jobject thiz) {
    jclass clazz = env->FindClass("com/example/MyClass"); // 每次都查找
    jmethodID mid = env->GetMethodID(clazz, "method", "()V");
    env->CallVoidMethod(thiz, mid);
}

// ✅ 好:缓存到全局变量
jclass g_clazz = NULL;
jmethodID g_mid = NULL;

JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
    JNIEnv* env;
    vm->GetEnv((void**)&env, JNI_VERSION_1_6);

    // 缓存jclass
    jclass localClazz = env->FindClass("com/example/MyClass");
    g_clazz = (jclass)env->NewGlobalRef(localClazz);
    env->DeleteLocalRef(localClazz);

    // 缓存jmethodID(methodID不需要创建全局引用)
    g_mid = env->GetMethodID(g_clazz, "method", "()V");

    return JNI_VERSION_1_6;
}

JNIEXPORT void JNICALL
Java_com_example_JNIHelper_method(JNIEnv* env, jobject thiz) {
    // 直接使用缓存的ID
    env->CallVoidMethod(thiz, g_mid);
}

14.11.3 使用Critical Native(Android 8.0+)

特点

  • 更快的JNI调用(无需JNIEnv参数)
  • 限制:不能调用JNI函数、不能触发GC

声明

// Java端使用@CriticalNative注解
@CriticalNative
public native int fastAdd(int a, int b);

实现

// 无需JNIEnv和jobject参数
extern "C" JNIEXPORT jint
Java_com_example_JNIHelper_fastAdd(jint a, jint b) {
    return a + b; // 只能使用C++原生操作
}

14.12 常见JNI错误

14.12.1 局部引用表溢出

错误信息

JNI ERROR: local reference table overflow (max=512)

原因
循环中创建大量局部引用未释放

解决

for (int i = 0; i < 100000; i++) {
    jstring str = env->NewStringUTF("Hello");
    // 使用str...
    env->DeleteLocalRef(str); // 释放局部引用
}

14.12.2 跨线程使用JNIEnv

错误信息

JNI WARNING: threadid=XXX using env from threadid=YYY

原因
在子线程使用主线程的JNIEnv

解决

// 在子线程中附加到JavaVM
JNIEnv* env;
g_jvm->AttachCurrentThread(&env, NULL);
// 使用env...
g_jvm->DetachCurrentThread();

14.12.3 返回错误的引用类型

错误

// ❌ 返回局部引用,函数返回后被释放
JNIEXPORT jobject JNICALL
Java_com_example_JNIHelper_getObject(JNIEnv* env, jobject thiz) {
    jclass clazz = env->FindClass("com/example/MyClass");
    return clazz; // 局部引用,返回后无效
}

// ✅ 返回全局引用
JNIEXPORT jobject JNICALL
Java_com_example_JNIHelper_getObject(JNIEnv* env, jobject thiz) {
    jclass localClazz = env->FindClass("com/example/MyClass");
    jclass globalClazz = (jclass)env->NewGlobalRef(localClazz);
    env->DeleteLocalRef(localClazz);
    return globalClazz; // 需要在Java端或后续调用中释放
}

14.13 实战案例

14.13.1 图像处理(像素操作)

Java端

public class ImageProcessor {
    static {
        System.loadLibrary("image-processor");
    }

    public native void processImage(Bitmap bitmap);
}

C++实现

#include <android/bitmap.h>

extern "C" JNIEXPORT void JNICALL
Java_com_example_ImageProcessor_processImage(JNIEnv* env, jobject thiz, jobject bitmap) {
    AndroidBitmapInfo info;
    void* pixels;

    // 获取Bitmap信息
    AndroidBitmap_getInfo(env, bitmap, &info);

    // 锁定Bitmap像素缓冲区
    AndroidBitmap_lockPixels(env, bitmap, &pixels);

    // 处理像素(例如:灰度化)
    uint32_t* pixel = (uint32_t*)pixels;
    for (int y = 0; y < info.height; y++) {
        for (int x = 0; x < info.width; x++) {
            uint32_t p = pixel[y * info.width + x];

            uint8_t r = (p >> 16) & 0xFF;
            uint8_t g = (p >> 8) & 0xFF;
            uint8_t b = p & 0xFF;

            // 灰度值 = 0.299R + 0.587G + 0.114B
            uint8_t gray = (uint8_t)(0.299 * r + 0.587 * g + 0.114 * b);

            pixel[y * info.width + x] = (0xFF << 24) | (gray << 16) | (gray << 8) | gray;
        }
    }

    // 解锁Bitmap
    AndroidBitmap_unlockPixels(env, bitmap);
}

CMakeLists.txt

find_library(jnigraphics-lib jnigraphics)
target_link_libraries(image-processor ${jnigraphics-lib})

14.13.2 音视频编解码(FFmpeg集成)

引入FFmpeg

# 添加预编译的FFmpeg库
add_library(avcodec SHARED IMPORTED)
set_target_properties(avcodec PROPERTIES IMPORTED_LOCATION
    ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libavcodec.so)

target_link_libraries(native-lib avcodec avformat avutil)

使用示例

extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
}

JNIEXPORT jint JNICALL
Java_com_example_VideoDecoder_decode(JNIEnv* env, jobject thiz, jstring path) {
    const char* filePath = env->GetStringUTFChars(path, NULL);

    AVFormatContext* formatCtx = avformat_alloc_context();
    avformat_open_input(&formatCtx, filePath, NULL, NULL);
    avformat_find_stream_info(formatCtx, NULL);

    // 解码逻辑...

    avformat_close_input(&formatCtx);
    env->ReleaseStringUTFChars(path, filePath);

    return 0;
}

本章总结

核心知识点

知识点 重要性 难度
JNI基本概念 ⭐⭐⭐⭐⭐ ⭐⭐
数据类型映射 ⭐⭐⭐⭐⭐ ⭐⭐
JNIEnv使用 ⭐⭐⭐⭐⭐ ⭐⭐⭐
方法签名 ⭐⭐⭐⭐ ⭐⭐⭐
引用类型管理 ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐
异常处理 ⭐⭐⭐⭐ ⭐⭐⭐
线程操作 ⭐⭐⭐⭐ ⭐⭐⭐⭐
性能优化 ⭐⭐⭐⭐ ⭐⭐⭐

开发流程速查

1. 在Java中声明native方法
   ↓
2. 加载so库:System.loadLibrary("native-lib")
   ↓
3. 编写C/C++实现(遵循命名规范)
   ↓
4. 配置CMakeLists.txt
   ↓
5. 编译生成.so库
   ↓
6. 在Java中调用native方法

常见应用场景

场景 说明 示例
图像处理 像素级操作性能要求高 美颜、滤镜、图像识别
音视频 编解码、格式转换 FFmpeg、OpenSL ES
游戏引擎 3D渲染、物理引擎 Unity、Cocos2d-x
加密算法 安全性要求高 RSA、AES加密
科学计算 大量数学运算 机器学习、信号处理
第三方库集成 复用C/C++库 OpenCV、Protobuf

面试高频问题

Q1: JNI的工作原理?
答:Java通过native关键字声明方法,JVM在运行时查找对应的C/C++函数(根据命名规范),通过JNIEnv调用。

Q2: 局部引用和全局引用的区别?
答:局部引用函数返回后自动释放,全局引用需要手动释放;局部引用不能跨线程,全局引用可以。

Q3: 如何在Native线程中调用Java方法?
答:使用JavaVM的AttachCurrentThread()获取JNIEnv,使用完后调用DetachCurrentThread()。

Q4: JNI性能优化的方法?
答:1) 减少JNI调用次数(批量处理);2) 缓存jclass和jmethodID;3) 使用Critical Native(Android 8.0+);4) 避免频繁的字符串转换。

Q5: 如何排查JNI崩溃?
答:1) 使用ndk-stack解析崩溃日志;2) 使用Android Studio的Native调试;3) 添加日志打印定位问题;4) 检查JNIEnv使用是否正确(线程安全、引用释放)。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐