Android NDK开发 JNI 基础

在android 系统开发中 ndk开发是很重要的内容。ndk开发中 jni 是基础。

目录

一.什么是JNI

[二. 如何使用JNI](#二. 如何使用JNI)

[1.Java 代码如何调用 c/c++ 代码](#1.Java 代码如何调用 c/c++ 代码)

[2. c/c++如何调用 java 函数](#2. c/c++如何调用 java 函数)


一. 什么是JNI

JNI------Java Native Interface,它是Java平台的一个特性(并不是Android系统特有的)。其实主要是定义了一些JNI函数,让开发者可以通过调用这些函数实现Java代码调用C/C++的代码,C/C++的代码也可以调用Java的代码,这样就可以发挥各个语言的特点了。

二. 如何使用JNI

一般情况下我们首先是将写好的C/C++代码编译成对应平台的动态库(windows一般是dll文件,linux一般是so文件等),这里我们是针对Android平台,所以只讨论so库。由于JNI编程支持C和C++编程,这里我们的栗子都是使用C++,对于C的版本可能会有些差异,但是主要的内容还是一致的。我们从两方面来介绍JNI, 一是 JAVA代码如何调用 C/C++ 代码。 一是 C/C++ 代码如何调用java代码。

1.Java 代码如何调用 c/c++ 代码

这里我们开发工具使用 android studio, android studio自带单步调试功能。能清晰的跟踪代码调用流程。Java 代码调用 c/c++代码有两种方式,静态注册和 动态注册。

静态注册,当我们在Android studio "new project "选 "native c++", 一路next, 创建出的这个例子 用的是 静态注册。 静态注册的优点是比动态注册方便一点, 缺点也明显, 当native方法比较多时, 写对应的jni c++ 方法会比较繁琐。

java 文件的写法

cpp 复制代码
    static {
        System.loadLibrary("myapplication"); 
    }

    public native String stringFromJNI();

c++ 这边的写法

cpp 复制代码
extern "C" JNIEXPORT jstring JNICALL
Java_com_unionpower_myapplication_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

动态注册 的关键是 jni_onload函数, 当我们使用System.loadLibarary()方法加载so库的时候,Java虚拟机就会找到这个函数并调用该函数,在该函数中做一些初始化的动作。

cpp 复制代码
static JNINativeMethod nativeMethods[] = {
        {"_init", "()Z", (void *) init},
        {"_sendMcuData", "([B)Z", (void*) sendMcuData},
        {"_getMcuData", "(II)[B", (void*) getMcuData},
        {"_deinit", "()Z",(void *) deinit},
};


JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
//    JNIEnv *env = AndroidRuntime::getJNIEnv();// 在android 系统中通过AndroidRuntiem获得JNIEnv
//    AndroidRuntime::registerNativeMethods(env, MCU_SERVER_NAME_FOR_APP, nativeMethods, NELEM(nativeMethods));
//    return JNI_VERSION_1_4;
    JNIEnv *env; //在android studio 中通过vm ->getEnv 得到JNIEnv
    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
        return -1;
    }
    jclass clz = env->FindClass(MCU_SERVER_NAME_FOR_APP);
    env->RegisterNatives(clz, nativeMethods, sizeof(nativeMethods)/sizeof(nativeMethods[0]));
    return JNI_VERSION_1_4;
}
cpp 复制代码
typedef struct {
    const char* name;
    const char* signature;
    void*       fnPtr;
} JNINativeMethod;
2. c/c++如何调用 java 函数

c/c++ 调用 java 的方式 是先得到类的字节码,

cpp 复制代码
jclass clazz = env->GetObjectClass(obj);
gClassInfo.autoImage = (jclass) env->NewGlobalRef(clazz);

获取方法ID,

cpp 复制代码
gClassInfo.callback = env->GetStaticMethodID(clazz, "callbackFromNative", "(I[BI)V");
复制代码
反射的方式调用到 java 里的方法
cpp 复制代码
env->CallStaticVoidMethod(gClassInfo.autoImage, gClassInfo.callback, BUSINESS_MCU_UPDATE, data, BUSINESS_MCU_UPDATE);

java 文件也要有对应的方法

cpp 复制代码
    private static void callbackFromNative(int msg, byte[] data, int value) {}

在android studio中单步调试 JNI 时, 如果遇到 socket 这类错误, 可以 选 build->clean project, 再重新运行即可。

相关推荐
你不是我我2 小时前
【Java 开发日记】HTTP3 性能更好,为什么内网微服务依然多用 HTTP2?HTTP2 内网优势是什么?
java·开发语言·微服务
雪碧聊技术3 小时前
大模型爆火!Java后端如何抓住Agent全栈开发的风口
java·大模型·agent·全栈开发
赏金术士3 小时前
Kotlin 数据流与单双向绑定
android·开发语言·kotlin
逻辑驱动的ken4 小时前
Java高频面试场景题25
java·开发语言·深度学习·面试·职场和发展
小白学鸿蒙4 小时前
Unity 3D 2023解压安装,配置安卓运行环境后打包安卓应用(踩坑无数之差点放弃)
android·unity·游戏引擎
AI人工智能+电脑小能手5 小时前
【大白话说Java面试题】【Java基础篇】第32题:Java的异常处理机制是什么
java·开发语言·后端·面试
阿巴斯甜5 小时前
2026小知识点(9)
android
爱吃土豆的马铃薯ㅤㅤㅤㅤㅤㅤㅤㅤㅤ6 小时前
通过java后端代码来实现给word内容补充格式文本内容控件,以及 设置控件的标记和标题
java·c#·word
古月-一个C++方向的小白7 小时前
MySQL数据库——数据类型
android·数据库·mysql