JNI 开发

我来详细讲解JNI和NDK开发的完整流程。

## 一、JNI和NDK概述

### JNI (Java Native Interface)

- Java本地接口,允许Java代码与其他语言(主要是C/C++)编写的代码进行交互

- 是Java平台的一部分,不限于Android

### NDK (Native Development Kit)

- Android原生开发工具包

- 提供了在Android上使用C/C++的工具链

- 包含编译器、调试器和构建脚本

## 二、JNI开发流程

### 1. 创建Java类并声明native方法

```java

package com.example.jnidemo;

public class NativeLib {

    // 加载so库

    static {

        System.loadLibrary("native-lib");

    }


    // 声明native方法

    public native String stringFromJNI();

    public native int add(int a, int b);

    public native void callJavaMethod();

}

```

### 2. 生成JNI头文件(可选,现在通常直接写)

```bash

# 老方法:使用javah生成头文件

javah -jni com.example.jnidemo.NativeLib

```

### 3. 实现C/C++代码

```cpp

#include <jni.h>

#include <string>

#include <android/log.h>

#define LOG_TAG "NativeLib"

#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)

extern "C" {

// 标准命名规则:Java_包名_类名_方法名

JNIEXPORT jstring JNICALL

Java_com_example_jnidemo_NativeLib_stringFromJNI(

        JNIEnv* env,

        jobject /* this */) {

    std::string hello = "Hello from C++";

    return env->NewStringUTF(hello.c_str());

}

JNIEXPORT jint JNICALL

Java_com_example_jnidemo_NativeLib_add(

        JNIEnv* env,

        jobject /* this */,

        jint a,

        jint b) {

    return a + b;

}

}

```

## 三、JNI加载和初始化流程

### 最先执行的函数:JNI_OnLoad

```cpp

// 当System.loadLibrary()被调用时,JNI_OnLoad是第一个被执行的函数

JNIEXPORT jint JNICALL

JNI_OnLoad(JavaVM* vm, void* reserved) {

    LOGI("JNI_OnLoad called");


    JNIEnv* env;

    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {

        return JNI_ERR;

    }


    // 可以在这里注册native方法(动态注册)

    registerNativeMethods(env);


    // 返回JNI版本

    return JNI_VERSION_1_6;

}

// 对应的卸载函数

JNIEXPORT void JNICALL

JNI_OnUnload(JavaVM* vm, void* reserved) {

    LOGI("JNI_OnUnload called");

}

```

## 四、Native方法注册方式

### 1. 静态注册(默认方式)

按照命名规则编写函数即可:

```cpp

// Java_包名_类名_方法名

JNIEXPORT jstring JNICALL

Java_com_example_jnidemo_NativeLib_stringFromJNI(JNIEnv* env, jobject thiz) {

    return env->NewStringUTF("Hello");

}

```

### 2. 动态注册(推荐)

```cpp

// Native方法实现

static jstring nativeGetString(JNIEnv* env, jobject thiz) {

    return env->NewStringUTF("Hello from dynamic registration");

}

static jint nativeAdd(JNIEnv* env, jobject thiz, jint a, jint b) {

    return a + b;

}

// 方法映射表

static JNINativeMethod methods[] = {

    {"stringFromJNI", "()Ljava/lang/String;", (void*)nativeGetString},

    {"add", "(II)I", (void*)nativeAdd}

};

// 注册函数

static int registerNativeMethods(JNIEnv* env) {

    jclass clazz = env->FindClass("com/example/jnidemo/NativeLib");

    if (clazz == NULL) {

        return JNI_FALSE;

    }


    if (env->RegisterNatives(clazz, methods,

            sizeof(methods) / sizeof(methods[0])) < 0) {

        return JNI_FALSE;

    }


    return JNI_TRUE;

}

```

## 五、JNI数据类型映射

### 基本类型映射

```cpp

// Java类型 -> JNI类型

boolean -> jboolean

byte    -> jbyte

char    -> jchar

short  -> jshort

int    -> jint

long    -> jlong

float  -> jfloat

double  -> jdouble

void    -> void

```

### 引用类型

```cpp

// 所有Java对象都是jobject

String    -> jstring

Object[]  -> jobjectArray

boolean[]  -> jbooleanArray

byte[]    -> jbyteArray

// ... 其他数组类型

Class      -> jclass

Throwable  -> jthrowable

```

## 六、JNI常用操作

### 1. 字符串操作

```cpp

// Java String -> C字符串

jstring jstr = ...;

const char* cstr = env->GetStringUTFChars(jstr, NULL);

// 使用cstr

env->ReleaseStringUTFChars(jstr, cstr);

// C字符串 -> Java String

const char* cstr = "Hello";

jstring jstr = env->NewStringUTF(cstr);

```

### 2. 数组操作

```cpp

// 获取数组

jintArray arr = ...;

jint* elements = env->GetIntArrayElements(arr, NULL);

jsize length = env->GetArrayLength(arr);

// 处理数组

for (int i = 0; i < length; i++) {

    elements[i] = elements[i] * 2;

}

// 释放数组

env->ReleaseIntArrayElements(arr, elements, 0);

```

### 3. 调用Java方法

```cpp

JNIEXPORT void JNICALL

Java_com_example_jnidemo_NativeLib_callJavaMethod(JNIEnv* env, jobject thiz) {

    // 获取类

    jclass clazz = env->GetObjectClass(thiz);


    // 获取方法ID

    jmethodID methodId = env->GetMethodID(clazz, "callback", "(Ljava/lang/String;)V");


    // 调用方法

    jstring msg = env->NewStringUTF("Hello from Native");

    env->CallVoidMethod(thiz, methodId, msg);


    // 清理本地引用

    env->DeleteLocalRef(msg);

}

```

## 七、NDK项目配置

### 1. CMakeLists.txt

```cmake

cmake_minimum_required(VERSION 3.10.2)

project("jnidemo")

# 添加库

add_library(

    native-lib

    SHARED

    native-lib.cpp

)

# 查找日志库

find_library(

    log-lib

    log

)

# 链接库

target_link_libraries(

    native-lib

    ${log-lib}

)

```

### 2. build.gradle配置

```gradle

android {

    defaultConfig {

        externalNativeBuild {

            cmake {

                cppFlags ""

            }

        }

        ndk {

            abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'

        }

    }


    externalNativeBuild {

        cmake {

            path "src/main/cpp/CMakeLists.txt"

            version "3.10.2"

        }

    }

}

```

## 八、完整示例

### Java端

```java

public class MainActivity extends AppCompatActivity {

    private NativeLib nativeLib;


    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);


        nativeLib = new NativeLib();


        // 调用native方法

        String result = nativeLib.stringFromJNI();

        int sum = nativeLib.add(10, 20);


        // 设置回调

        nativeLib.setCallback(new NativeLib.Callback() {

            @Override

            public void onMessage(String msg) {

                Log.d("MainActivity", "Received: " + msg);

            }

        });


        nativeLib.triggerCallback();

    }

}

```

### Native端

```cpp

#include <jni.h>

#include <string>

// 保存JavaVM指针

static JavaVM* g_vm = nullptr;

static jobject g_callback = nullptr;

JNIEXPORT jint JNICALL

JNI_OnLoad(JavaVM* vm, void* reserved) {

    g_vm = vm;

    return JNI_VERSION_1_6;

}

JNIEXPORT void JNICALL

Java_com_example_jnidemo_NativeLib_setCallback(JNIEnv* env, jobject thiz, jobject callback) {

    // 保存全局引用

    if (g_callback != nullptr) {

        env->DeleteGlobalRef(g_callback);

    }

    g_callback = env->NewGlobalRef(callback);

}

JNIEXPORT void JNICALL

Java_com_example_jnidemo_NativeLib_triggerCallback(JNIEnv* env, jobject thiz) {

    if (g_callback == nullptr) return;


    jclass clazz = env->GetObjectClass(g_callback);

    jmethodID method = env->GetMethodID(clazz, "onMessage", "(Ljava/lang/String;)V");


    jstring msg = env->NewStringUTF("Hello from Native!");

    env->CallVoidMethod(g_callback, method, msg);

    env->DeleteLocalRef(msg);

}

```

## 九、调试技巧

### 1. 日志输出

```cpp

#include <android/log.h>

#define LOG_TAG "NativeDebug"

#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)

#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)

LOGD("Value: %d", value);

```

### 2. 异常处理

```cpp

// 检查异常

if (env->ExceptionCheck()) {

    env->ExceptionDescribe();

    env->ExceptionClear();

    return;

}

// 抛出异常

jclass exClass = env->FindClass("java/lang/IllegalArgumentException");

env->ThrowNew(exClass, "Invalid argument");

```

## 总结

1. **加载顺序**:System.loadLibrary() → JNI_OnLoad() → Native方法调用

2. **JNI_OnLoad**是最先执行的函数,用于初始化和动态注册

3. 推荐使用动态注册方式,更灵活且不依赖命名规则

4. 注意内存管理,及时释放本地引用

5. 合理使用全局引用保存Java对象

6. 做好异常处理,避免崩溃

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容