JNI中调用Java函数

简介: JNI中调用Java函数

一、JNI 注册

    JNI 分成静态注册和动态注册

  1. 静态注册

    cpp 实现

JNIEXPORT jstring JNICALL
Java_com_example_hellojni_HelloJni_stringFromJNI(JNIEnv *env, jobject thiz) {
  // ...
}

Java 中使用

package com.example.hellojni
class HelloJni : AppCompatActivity() {
  // ...
  override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    // ...
        binding.helloTextview.text = stringFromJNI()
    }
    
  external fun stringFromJNI(): String?
  companion object {
        init {
            System.loadLibrary("hello-jni")
        }
    }
}
  1. 动态注册
// 动态注册
namespace android {

static jobject android_os_MemoryFile_open(JNIEnv* env, jobject clazz, jstring name, jint length) {
  // ...
  }
}

static const JNINativeMethod methods[] = {
    {"native_open",  "(Ljava/lang/String;I)Ljava/io/FileDescriptor;", (void*)android_os_MemoryFile_open},
    {"native_mmap",  "(Ljava/io/FileDescriptor;II)I", (void*)android_os_MemoryFile_mmap},
    {"native_munmap", "(II)V", (void*)android_os_MemoryFile_munmap},
    {"native_close", "(Ljava/io/FileDescriptor;)V", (void*)android_os_MemoryFile_close},
    {"native_read",  "(Ljava/io/FileDescriptor;I[BIIIZ)I", (void*)android_os_MemoryFile_read},
    {"native_write", "(Ljava/io/FileDescriptor;I[BIIIZ)V", (void*)android_os_MemoryFile_write},
    {"native_pin",   "(Ljava/io/FileDescriptor;Z)V", (void*)android_os_MemoryFile_pin},
    {"native_get_size", "(Ljava/io/FileDescriptor;)I",
            (void*)android_os_MemoryFile_get_size}
};

int AndroidRuntime::registerNativeMethods(JNIEnv* env,
    const char* className, const JNINativeMethod* gMethods, int numMethods)

5分钟理解Android中的JNI原理

二、JNI 调用 Java 函数

    JNI 调用 Java 函数,主要是在 JNI 中使用反射调用 Java 中的函数。

1、实例

  1. Java代码:
package com.my.hawk.jni2;
 
import androidx.appcompat.app.AppCompatActivity;
 
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
 
import static java.lang.String.format;
 
public class MainActivity extends AppCompatActivity {
 
    TextView tv;
    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib");
    }
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        // Example of a call to a native method
        tv = findViewById(R.id.sample_text);
        tv.setText(stringFromJNI());
 
        nativeInitilize();
 
        Button startBt = findViewById(R.id.button);
        startBt.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                nativeThreadStart();
            }
        });
 
        Button stopBt = findViewById(R.id.button2);
        stopBt.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                nativeThreadStop();
            }
        });
    }
 
    public void onNativeCb(int count) {
        Log.d("Native", "onNativeCb count=" + count);
//        TextView tv = findViewById(R.id.sample_text);
//        tv.setText(format("%s%d", stringFromJNI(), count));
        tv.post(new Runnable() {
            @Override
            public void run() {
                tv.setText(format("%s%d", stringFromJNI(), count));
            }
        });
    }
 
    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    public native String stringFromJNI();
 
    public native void nativeInitilize();
    public native void nativeThreadStart();
    public native void nativeThreadStop();
}
  1. JNI代码
#include <jni.h>
#include <string>
#include <sstream>
#include <android/log.h>
#include <unistd.h>
 
JavaVM *gJavaVm;
jobject gJaveObj;
static volatile int gIsThreadExit = 0;
 
#define LOG(...) __android_log_print(ANDROID_LOG_DEBUG, "Native", __VA_ARGS__)
 
static const char *classPath = "com/my/hawk/jni2/MainActivity";
 
extern "C" JNIEXPORT jstring JNICALL
Java_com_my_hawk_jni2_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}
 
extern "C"
JNIEXPORT void JNICALL
Java_com_my_hawk_jni2_MainActivity_nativeInitilize(JNIEnv *env, jobject thiz) {
    env->GetJavaVM(&gJavaVm);
    gJaveObj = env->NewGlobalRef(thiz);
}
 
static void* native_thread_exec(void *arg) {
    JNIEnv *env;
    gJavaVm->AttachCurrentThread(&env, nullptr);
 
//    jclass javaClass = env->FindClass(classPath);
    jclass javaClass = env->GetObjectClass(gJaveObj);
    if (javaClass == nullptr) {
        LOG("Fail to find javaClass");
        return nullptr;
    }
 
    jmethodID javaCallback = env->GetMethodID(javaClass, "onNativeCb", "(I)V");
    if (javaCallback == nullptr) {
        LOG("Fail to find method onNativeCb");
        return nullptr;
    }
 
    LOG("native_thread_exec loop enter");
 
    int count = 0;
    while (!gIsThreadExit) {
        env->CallVoidMethod(gJaveObj, javaCallback, count++);
        sleep(1);
    }
    gJavaVm->DestroyJavaVM();
    LOG("native_thread_exec loop leave");
 
    return nullptr;
}
 
extern "C"
JNIEXPORT void JNICALL
Java_com_my_hawk_jni2_MainActivity_nativeThreadStart(JNIEnv *env, jobject thiz) {
    gIsThreadExit = 0;
    pthread_t threadId;
    if (pthread_create(&threadId, nullptr, native_thread_exec, nullptr) != 0) {
        LOG("native_thread_start pthread_create fail!");
        return;
    }
    LOG("native_thread_start success");
}
 
extern "C"
JNIEXPORT void JNICALL
Java_com_my_hawk_jni2_MainActivity_nativeThreadStop(JNIEnv *env, jobject thiz) {
    gIsThreadExit = 1;
    LOG("native_thread_stop success");
}

    其中的关键,获取方法,然后通过反射调用 native_thread_exec,初始化的时候保存全局 JVMclass 对象。

    env->GetJavaVM(&gJavaVm);
    gJaveObj = env->NewGlobalRef(thiz);

2、总结

  1. Android 环境中,每个进程只能诞生一个 JavaVM 对象,被所有线程共享。在 VM 加载 *.so 程序库时,会先调用 JNI_OnLoad() 函数,在 JNI_OnLoad() 函数中会将 JavaVM 指针对象保存到 CJNI 的全局变量中。
  2. JNIEnv 对象和线程是一一对应的关系;
  3. JvmJNIEnv 释放问题?JVMJava Heap 的内存泄漏?JVM 内存中 native memory 的内存泄漏?

4.从操作系统角度看,JVM 在运行时和其它进程没有本质区别。在系统级别上,它们具有同样的调度机制,同样的内存分配方式,同样的内存格局。JVM 进程空间中,Java Heap 以外的内存空间称为 JVM 的 native memory进程的很多资源都是存储在 JVM 的 native memory 中,例如载入的代码映像,线程的堆栈,线程的管理控制块,JVM 的静态数据、全局数据等等。也包括 JNI 程序中 native code 分配到的资源。

Local Reference 导致的内存泄漏?

3、参考

Android开发实践:JNI层线程回调Java函数示例 - 指针空间 - 博客园

JNI开发:JNI层新起的函数中(C回调函数中)调用JAVA层的接口_tingzhushaohua的博客-CSDN博客_jni 回调函数

C++通过JNI层回调java函数 - 百度文库

Android NDK开发(一) - 简书

jni java 函数指针_java native interface JNI 调用Java方法_我是XiaoYang呀的博客-CSDN博客

三、JNI 数据传递

Android:JNI调用C++自定义类的详细方法_chaoqiangscu的博客-CSDN博客_jni调用c++类

Java代码与Jni层之间传递数组(byte[])_xiao慕r的博客-CSDN博客_jni传递数组

Android-JNI之数据类型转换_zhezi521的博客-CSDN博客_android jni 类型转换

android ndk 返回字符串,android ndk返回String(字符串)_天才娜娜ln的博客-CSDN博客

小心ReleaseByteArrayElements 中的参数问题_普通网友的博客-CSDN博客

JNIEnv*的常用函数详解

java jni 手册_Java中JNI的使用详解第二篇:JNIEnv类型和jobject类型的解释_发条粽子的博客-CSDN博客

NDK 开发之 Bitmap 的使用-技术圈

Android之OpenCv简单人脸识别功能(Bitmap)_路和远方的博客-CSDN博客_android opencv 人脸识别

JNI层向Java层传递bitmap | 码农家园

android中通过JNI读取Bitmap文件,并调用opencv进行处理_一天到晚游泳的鱼啊鱼的博客-CSDN博客

JNI String类型 - 知乎

JNI 通过形参String返回数据的方法_Cosmo_Wang1989的博客-CSDN博客_jni 形参返回字符串

简介Bitmap、YUV,NV21与Bitmap互转_XDK-Net的博客-CSDN博客_bitmap转nv21

bitmap 转换nv21_驱梦人的博客-CSDN博客_bitmap转nv21

JNI层向Java层传递bitmap | 码农家园

计算Java函数的签名 |

Android开发实践:JNI函数签名生成器 - 行业资讯 - 肥雀云_南京肥雀信息技术有限公司


native和static native区别_飞鸟_的博客-CSDN博客_jni static

四、JNA

JNI便捷开发框架JNA框架之入门(一)_cy谭的博客-CSDN博客_jna

JNI便捷开发框架JNA框架之指针参数Pointer(二)_cy谭的博客-CSDN博客_jna pointer

JNI便捷开发框架JNA框架之引用传递ByReference(三)_cy谭的博客-CSDN博客

JNI便捷开发框架JNA框架之结构参数体传递(四)_cy谭的博客-CSDN博客_jna 结构体传参

JNA传递二维指针数组参数给C语言_Xeon_CC的博客-CSDN博客_jna传递数组给c

JNA 技术解密_ccfeng2008的博客-CSDN博客_jna原理

jna编程学习 - 走看看

JNA 使用 回调函数 - 博麗靈夢 - 博客园

java高级用法之:JNA中的回调_flydean程序那些事的博客-CSDN博客_jna 回调函数

Jna及如何调试_nanshenjiang的博客-CSDN博客_jna测试

libffi浅析_ayu_ag的博客-CSDN博客_libffi

使用 libffi 实现 AOP_diaoju3333的博客-CSDN博客

【libffi】动态调用&定义C函数_Yaso_GG的博客-CSDN博客_libffi

深入浅出JNA

android下使用JNA_10km的博客-CSDN博客_android jna

Ubuntu 12.04下制作JNA For Android_齐北的博客-CSDN博客

五、图像传递

移动端视频进阶(三):OpenCV的集成及视频帧转cv::Mat的相关操作_木大白易的博客-CSDN博客

Android 相机 NV21 byte[] 和 JPEG byte[] 转 OpenCV 的 Mat_weixin_33973609的博客-CSDN博客

在IOS上YUV NV21格式的CVPixelBufferRef转opencv的RGB格式cv::Mat的方法_星辰辰大海的博客-CSDN博客

目录
相关文章
|
9天前
|
安全 Java API
【性能与安全的双重飞跃】JDK 22外部函数与内存API:JNI的继任者,引领Java新潮流!
【9月更文挑战第7天】JDK 22外部函数与内存API的发布,标志着Java在性能与安全性方面实现了双重飞跃。作为JNI的继任者,这一新特性不仅简化了Java与本地代码的交互过程,还提升了程序的性能和安全性。我们有理由相信,在外部函数与内存API的引领下,Java将开启一个全新的编程时代,为开发者们带来更加高效、更加安全的编程体验。让我们共同期待Java在未来的辉煌成就!
35 11
|
10天前
|
安全 Java API
【本地与Java无缝对接】JDK 22外部函数和内存API:JNI终结者,性能与安全双提升!
【9月更文挑战第6天】JDK 22的外部函数和内存API无疑是Java编程语言发展史上的一个重要里程碑。它不仅解决了JNI的诸多局限和挑战,还为Java与本地代码的互操作提供了更加高效、安全和简洁的解决方案。随着FFM API的逐渐成熟和完善,我们有理由相信,Java将在更多领域展现出其强大的生命力和竞争力。让我们共同期待Java编程新纪元的到来!
33 11
|
19天前
|
Java 调度 Android开发
Android经典实战之Kotlin的delay函数和Java中的Thread.sleep有什么不同?
本文介绍了 Kotlin 中的 `delay` 函数与 Java 中 `Thread.sleep` 方法的区别。两者均可暂停代码执行,但 `delay` 适用于协程,非阻塞且高效;`Thread.sleep` 则阻塞当前线程。理解这些差异有助于提高程序效率与可读性。
39 1
|
2月前
|
存储 Java 编译器
Java中ArrayList的常用函数
确切地说,`ArrayList` 提供的这些方法构成了一套强大并且灵活的工具集,可以满足各种程序设计情况中的需求。例如,通过使用 `iterator()`方法,开发者可以在不知道集合大小的情况下遍历集合中全部或部分元素;而 `sort()`方法则能够对集合中的元素进行排序。这些函数在日常的Java编程中极其常见且重要,掌握它们对于进行集合操作和数据处理来说是基础且必须的。
22 2
Java中ArrayList的常用函数
|
18天前
|
存储 运维 Java
函数计算产品使用问题之怎么配置定时触发器来调用Java函数
函数计算产品作为一种事件驱动的全托管计算服务,让用户能够专注于业务逻辑的编写,而无需关心底层服务器的管理与运维。你可以有效地利用函数计算产品来支撑各类应用场景,从简单的数据处理到复杂的业务逻辑,实现快速、高效、低成本的云上部署与运维。以下是一些关于使用函数计算产品的合集和要点,帮助你更好地理解和应用这一服务。
|
21天前
|
开发框架 Java Android开发
JNI中调用Java函数
JNI中调用Java函数
22 0
|
2月前
|
Rust Cloud Native Java
Java演进问题之Serverless应用或函数的冷启动如何解决
Java演进问题之Serverless应用或函数的冷启动如何解决
|
1月前
|
算法 Java Linux
Intellij Java JNI 调用 C++
Intellij Java JNI 调用 C++
24 0
|
2月前
|
存储 Java Unix
(八)Java网络编程之IO模型篇-内核Select、Poll、Epoll多路复用函数源码深度历险!
select/poll、epoll这些词汇相信诸位都不陌生,因为在Redis/Nginx/Netty等一些高性能技术栈的底层原理中,大家应该都见过它们的身影,接下来重点讲解这块内容。
|
2月前
|
Java 测试技术
在Java中使用断言函数进行代码测试
在Java中使用断言函数进行代码测试