JNI入门基础

2023-11-20

环境安装

想要使用jni进行ndk开发,我们首先要安装下面这些工具,否则直接从入门到放弃。

  • 下载ndk支持
    在这里插入图片描述
    在Android studio中下载上图中框选的两个工具,版本号自己任意选一个。下载完成之后,Android Studio就拥有了进行ndk编译的能力。

  • 新建ndk工程
    如果是一个全新的工程,我们在新建工程时,选择c++工程,然后无脑下一步即可,这样Android Studio就会给你生成一个c++工程的模板代码,我们后续要添加一些c/c++代码都可以仿照这个模板。
    在这里插入图片描述
    在这里插入图片描述
    如果,我们是现在原有的工程上添加一个c/c++的library呢?我们可以新建一个native library,然后也是无脑下一步,跟新建一个java的library流程一致。
    在这里插入图片描述

  • c/c++代码提示设置
    在我们编写c/c++的过程中,有一个十分恶心的东西,就是没有代码提示,有时候编译器抽风,无故报红。这对一些ndk老手来说可能不算什么,但是对一些新手来说就十分难受了,那么我们该如何解决这个问题呢?
    在这里插入图片描述

JNI与NDK

jni,即Java本地接口,它的存在使得Java与本地其他类型语言(如c、c++)能够进行交互。java的很多功能实际上的驱动都是通过c/c++开发的,通过JNI,Java可以调用c/c++实现的驱动,从而扩展jvm的能力。另外,在高效率的数学运算、游戏的实时渲染、音视频的编码和解码方面,一般都是用c开发的。

  • jni在线api:https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/jniTOC.html

  • jni和ndk的关系

    1. jni是jdk提供的一套非常强大的框架,可供c/c++与java互相调用,一般jni的头文件在jdk安装路径的include文件夹里面。
    2. ndk是Android平台提供的Native开发工具集开发包,可以认为是把前面的jni额外又封装了一层。一般ndk中的jni安装在如下目录下:C:\Users\dell\AppData\Local\Android\Sdk\ndk\21.4.7075529\toolchains\llvm\prebuilt\windows-x86_64\sysroot\usr\include
  • 案例讲解
    我们来看看ndk开发中涉及到的几个文件:

    1. cpp/CMakeLists.txt
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html

# Sets the minimum version of CMake required to build the native library.

cmake_minimum_required(VERSION 3.18.1)

# Declares and names the project.

project("jnidemo")

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.

add_library( # Sets the name of the library.
        jnidemo

        # Sets the library as a shared library.
        SHARED

        # Provides a relative path to your source file(s).
        native-lib.cpp)

# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.

find_library( # Sets the name of the path variable.
        log-lib

        # Specifies the name of the NDK library that
        # you want CMake to locate.
        log)

# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.

target_link_libraries( # Specifies the target library.
        jnidemo

        # Links the target library to the log library
        # included in the NDK.
        ${log-lib})
  1. cpp/native-lib.cpp:我们所写的c/c++就是放在该文件中或者新建一个新的c/c++文件
#include <jni.h>
#include <string>

// 日志输出
#include <android/log.h>

#define TAG "Brett"

// __VA_ARGS__ 代表 ...的可变参数
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG,  __VA_ARGS__);
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG,  __VA_ARGS__);
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG,  __VA_ARGS__);

extern "C" JNIEXPORT jstring JNICALL
Java_com_mvp_jnidemo_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_mvp_jnidemo_MainActivity_changeStr(JNIEnv *env, jobject thiz) {
    //jclass获取方式一
//    jclass cls = env->FindClass("com/mvp/jnidemo/MainActivity");
    //方式二
    jclass cls = env->GetObjectClass(thiz);
    jfieldID nameFid = env->GetFieldID(cls, "name", "Ljava/lang/String;");

    jstring value = env->NewStringUTF("Beyond");
    env->SetObjectField(thiz, nameFid, value);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_mvp_jnidemo_MainActivity_changeAge(JNIEnv *env, jclass clazz) {
    jfieldID ageFid = env->GetStaticFieldID(clazz, "age", "I");

    int age = env->GetStaticIntField(clazz, ageFid);
    env->SetStaticIntField(clazz, ageFid, age + 10);

}

//c调用java
extern "C"
JNIEXPORT jint JNICALL
Java_com_mvp_jnidemo_MainActivity_callJavaMethod(JNIEnv *env, jobject thiz) {
    jclass mainActivitCls = env->GetObjectClass(thiz);

    // jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
    jclass cls = env->GetObjectClass(thiz);
    jmethodID addMid = env->GetMethodID(cls, "add", "(II)I");

    // jint CallIntMethod(jobject obj, jmethodID methodID, ...)
    int result = env->CallIntMethod(thiz, addMid, 1, 1);
    LOGD("result:%d\n", result)
 // ++++++++++++++++++++++ C调用 public String showString(String str,int value) 函数


    jmethodID showStringMid = env->GetMethodID(mainActivitCls, "showString", "(Ljava/lang/String;I)Ljava/lang/String;");

    // jobject     (*CallObjectMethod)(jobject, jmethodID, ...);
    jstring value = env->NewStringUTF("李元霸");
    jstring resultStr = (jstring) env->CallObjectMethod(mainActivitThis, showStringMid, value, 9527);
    // jstring是在jni中的,c/c++中需要转为char*
    const char * resultCstr = env->GetStringUTFChars(resultStr, NULL);
    LOGD("r==:%s\n", resultCstr);
}

extern “C” // 表示下面的代码,采用C的编译方式 如果是新建了一个.c文件,则需要删掉否则编译会报错,如果是新建了一个c++文件,则需要添加
JNIEXPORT // JNIEXPORT: JNI重要标记关键字,不能少 可以认为是为不同os平台设定的一个调用规则(标记为该方法可以被外部调用) Windows内部规则,与Linux内部规则 不同
void // void 代表java中的 void
JNICALL // 也是一个关键字,(可以少的) jni call (约束函数入栈顺序,和堆栈内存清理的规则)
JNIEnv *env JNI JNIEnv是整个jni的核心所在,我们学习jni其实就是学习JNIEnv里面提供的api方法

如果当前是 native-lib.c
// (*env)->xxx函数
// (*env)->DeleteLocalRef()
// C语言是 JNIEnv *env 二级指针
// (*env)->DeleteLocalRef(env, NULL); // C是没有对象的,想持有env环境,就必须传递进去
(*env)->NewStringUTF(env,“AAAA”);
如果当前是 native-lib.cpp ->调用一级指针下的函数
// env->xxx函数
// env->DeleteLocalRef()
// C++语言是 JNIEnv *env 一级指针
// env->DeleteLocalRef(NULL); // C++是有对象的,本来就会持有this,所以不需要传

为简单通用起见,我们就使用c++语言来编写相关的逻辑。

  1. 调用者(拥有native方法的类)——>MainActivity
package com.mvp.jnidemo;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

import com.mvp.jnidemo.databinding.ActivityMainBinding;

public class MainActivity extends AppCompatActivity {
    private String name = "Brett";
    private static int age = 20;

    // Used to load the 'jnidemo' library on application startup.
    //可以认为哪里又native方法,哪里就需要System.loadLibrary("xxx");方法
    static {
        System.loadLibrary("jnidemo");
    }

    private ActivityMainBinding binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        // Example of a call to a native method
        TextView tv = binding.sampleText;
        tv.setText(stringFromJNI());
        Log.e("MainActivity", "name is " + name+" ,age is "+age);
        changeStr();
        changeAge();
        Log.e("MainActivity", "name is " + name+" ,age is "+age);
        callJavaMethod();
    }
     public int add(int num1, int num2) {
        Log.e("MainActivity", "num1 is " + num1 + " ,num2 is " + num2);

        return num1 + num2;
    }
    public String showString(String str,int value) {
        System.out.println("C居然调用了我 showString str:" + str + " value:" + value);
        return "【" + str + "】";
    }

    /**
     * A native method that is implemented by the 'jnidemo' native library,
     * which is packaged with this application.
     */
    public native String stringFromJNI();

    public native void changeStr();

    public static native void changeAge();
        public native int callJavaMethod();

}

附:类型签名

代码 类型
“I” int
“B” byte
“C” char
“D” double
“F” float
“J” long
“S” short
“Z” boolean
“V” void
“[…;” 数组
“[[…;” 二维数组
“[[[…;” 三维数组
String[] [Ljava/lang/String;
String[][] [[Ljava/lang/String;
int,String,String[] ILjava/lang/String;[Ljava/lang/String;
int,boolean,long,String[],double IZJ[Ljava/lang/String;D
Class<?>,String,Object…paramType Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/Object;
int[] [I
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JNI入门基础 的相关文章

  • 加快 ImageView 中的缩放功能

    我目前正在处理非常大的图像 7 10mb 由于多种原因无法调整大小或压缩 现在 我们的想法是在自定义 ImageView 中显示它们 使用户能够进行双击缩放 捏合缩放等 我使用这个库来完成这项工作 https github com Mike
  • 通过列表视图检查动态生成的复选框时遇到问题

    我知道其他成员已经提出了这个问题 一些成员也给出了解决方案 但问题是我没有找到任何适合我的应用程序的解决方案 我正在创建一个应用程序 其中我有一个屏幕 它将显示动态列表视图 其中包含列表项 复选框和三个文本视图 一个用于候选人姓名 另外两个
  • 套接字的读写如何同步?

    我们创建一个套接字 在套接字的一侧有一个 服务器 在另一侧有一个 客户端 服务器和客户端都可以向套接字写入和读取 这是我的理解 我不明白以下事情 如果服务器从套接字读取数据 它在套接字中是否只看到客户端写入套接字的内容 我的意思是 如果服务
  • ACCESS_BACKGROUND_LOCATION 不适用于低于 Q (29) 的 Android 版本

    我的应用程序面向 Android API 28 根据文档 https developer android com preview privacy location target android 10 我应该要求ACCESS BACKGROU
  • 从 GitHub 上托管的 Spring Cloud Config Server 访问存储库的身份验证问题

    我在 GitHub 上的存储库中托管配置 如果我将回购公开 一切都好 但如果我将其设为私有 我将面临 org eclipse jgit errors TransportException https github com my user m
  • Java Swing For mac 中的 DJ Native Swing 浏览器

    我有一个用 Swing 制作的 Java 应用程序 并且使用了一个 DJ Native Swing 浏览器 当我尝试在 OS X 上使用它时 它抛出了一个NoClassDefFoundError尽管我添加了 swt jar 但始终如此 有人
  • 使用架构注册表对 avro 消息进行 Spring 云合约测试

    我正在查看 spring 文档和 spring github 我可以看到一些非常基本的内容examples https github com spring cloud samples spring cloud contract sample
  • 流中的非终结符 forEach() ?

    有时 在处理 Java Stream 时 我发现自己需要一个非终端 forEach 来触发副作用但不终止处理 我怀疑我可以用 map item gt f item 之类的方法来做到这一点 其中方法 f 执行副作用并将项目返回到流中 但这似乎
  • 在 Android 中上传文件出现内存不足错误

    我的上传代码如下 String end r n String twoHyphens String boundary try URL url new URL ActionUrl HttpURLConnection con HttpURLCon
  • Docker 和 Eureka 与 Spring Boot 无法注册客户端

    我有一个使用 Spring Boot Docker Compose Eureka 的非常简单的演示 我的服务器在端口 8671 上运行 具有以下应用程序属性 server port 8761 eureka instance prefer i
  • Spring-ws:如何从没有“Request”元素的 xsd 创建 Wsdl

    尝试为客户端实现 SOAP Web 服务 我需要一个 wsdl 文件来通过soapUI 测试该服务 但正如您在下面看到的 这个 xsd 没有 Request 和 Response 方法 所有请求和响应都被定义为基本 ServiceProvi
  • Java Swing:需要一个高质量的带有复选框的开发 JTree

    我一直在寻找一个 Tree 实现 其中包含复选框 其中 当您选择一个节点时 树中的所有后继节点都会被自动选择 当您取消选择一个节点时 树中其所有后继节点都会自动取消选择 当已经选择了父节点 并且从其后继之一中删除了选择时 节点颜色将发生变化
  • Hamcrest Matchers - 断言列表类型

    问题 我目前正在尝试使用 Hamcrest Matchers 来断言返回的列表类型是特定类型 例如 假设我的服务调用返回以下列表 List
  • OpenCSV:将嵌套 Bean 映射到 CSV 文件

    我正在尝试将 bean 映射到 CSV 文件 但问题是我的 bean 具有其他嵌套 bean 作为属性 所发生的情况是 OpenCSV 遍历属性找到一个 bean 然后进入其中并映射该 bean 内的所有数据 如果找到另一个 bean 它就
  • Android 中的 Google Places API - 适用于个人用户的 API_KEY

    我已经浏览了与在 Android 应用程序中使用 Places API 相关的 Android 文档和其他博客 到处都建议使用 API KEY 来调用 REST 服务 API KEY 在整个项目 应用程序中都是相同的 每天的请求数限制为 1
  • 如何重新启动死线程? [复制]

    这个问题在这里已经有答案了 有哪些不同的可能性可以带来死线程回到可运行状态 如果您查看线程生命周期图像 就会发现一旦线程终止 您就无法返回到新位置 So 没有办法将死线程恢复到可运行状态 相反 您应该创建一个新的 Thread 实例
  • 通过powershell运行ADB命令

    所以我尝试通过 powershell 脚本运行一些 ADB 命令 这是我正在尝试做的一个简单示例 adb shell echo in adb shell su root echo you are now root ls cd data da
  • org.apache.commons.net.io.CopyStreamException:复制时捕获 IOException

    我正在尝试使用以下方法中的代码将在我的服务器中创建的一些文件复制到 FTP 但奇怪的是我随机地低于错误 我无法弄清楚发生了什么 Exception org apache commons net io CopyStreamException
  • Java EE 目录结构

    我对以下教程有疑问 http www mkyong com jsf2 jsf 2 internationalization example http www mkyong com jsf2 jsf 2 internationalizatio
  • Spring表单ModelAttribute字段验证避免400 Bad Request错误

    我有一个ArticleFormModel包含正常发送的数据html form由 Spring 使用注入 ModelAttribute注释 即 RequestMapping value edit method RequestMethod PO

随机推荐

  • 两种接入微信小程序智能客服对话的方式

    微信小程序 此处提供两种接入微信小程序的方式 方式一 扫码将机器人绑定至指定小程序 通过小程序内的客服组件开启智能对话功能 方式二 通过小程序插件接入 方式一 后台扫码绑定 流程示意 效果展示 使用页面 小程序页面中 点击 客服按钮 官方组
  • QT 打印/输出 的 3种方法

    1 cout输出 include
  • 【复变函数】常用公式大全

    文章目录 基本公式 几个高斯的公式 其实都是留数法 留数法 一些公式 一些积分 欢迎纠错 基本公式 f z u
  • 华为5500v3多路径linux6,CentOS7 DM-Multipath+HUAWEI OceanStor存储多路径配置

    在参看了网上他人的配置 结合RedHat的技术白皮书实现后 自己亲身在CentOS7 HUAWEI OceanStore S2600T的基础上配置了一个DM Multipath存储多路径的实现 其中碰到了一些小问题和不明白的地方 书上感觉也
  • 5. TypeScript 接口

    TypeScript 接口 接口可以在面向对象编程中表示行为的抽象 也可以描述对象的形状 接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约 接口中不能含有具体的实现逻辑 1 函数接口参数 没有接口的写法 const fullNa
  • three.js开发全景视频播放器的现实方法

    业余弄弄three js 想用three js实现播放全景视频 研究了一段 搜索很多资料 感觉这一遍很棒 搜藏分享下 原理 将video标签拉伸显示在three js场景的一个球模型上 用相机在中间播放渲染 基础 基于three js官方案
  • IntelliJ IDEA 学习笔记 - 几个窗口布局设置技巧

    原文 https blog csdn net cgl125167016 article details 79000774 utm source copy IntelliJ IDEA 简体中文专题教程 编辑区分屏 IntelliJ IDEA
  • 下载Eclipse IDE

    提示 文章写完后 目录可以自动生成 如何生成可参考右边的帮助文档 文章目录 一 下载eclipse 二 安装语言包 一 下载eclipse Eclipse是一个开放源代码的 基于Java的可扩展开发平台 官方网站是https www ecl
  • 为 SQL Server 站点数据库服务器配置 SPN

    如何为 SQL Server 站点数据库服务器配置 SPN 主题上次更新时间 2008 年 1 月 使用 SQL Server 计算机的本地系统帐户运行 SQL Server 服务不是 SQL Server 最佳方案 为了最安全地运行 SQ
  • day 2

    定义一个学生的结构体 包含学生的姓名年龄 成绩 性别 学生的成绩姓名定义为私有权限 定义一个学生类型的结构体变量 设置共有函数用于给学生的成绩和名字进行赋值 include
  • 半导体成新资本洼地,国产化浪潮势不可挡

    配图来自Canva 近日 中芯国际成功登陆科创板 按照中芯国际发行价27 46元 股 发行16 86亿股计算 本次的募资金额是462 87亿元 比此前招股书规划的200亿元高出一倍多 在超额配售选择权行使后 发行总股数扩大至19 38亿股
  • Linux查看与挂载新磁盘

    问题 把CentOS都换成了Ubuntu Server 16 04 LTS 用df h查看磁盘占用情况 确发现之前插入的一块大容量磁盘 dev sdb1消失了 是磁盘坏了 还是没被系统识别 解决 1 用命令fdisk l查看新磁盘是否被系统
  • QSharedMemory在linux下异常崩溃导致的bug

    感谢这位博主 https blog csdn net xinluo7 article details 118226389 在Windows系统下 当程序出现崩溃 虽然没有主动调用QSharedMemory attach 但是系统会自动回收Q
  • Java+MyEclipse+Tomcat (三)配置MySQL及查询数据显示在JSP网页中

    前面两篇文章讲述了如何配置MyEclipse和Tomcat开发JSP网站 如何配置Servlet简单实现表单提交 这篇文章主要讲述配置MySQL实现数据库连接MyEclipse 最后将查询表中的数据显示在JSP网页中 文章主要以图片描述为主
  • Java 多线程编程(入门)

    目录 一 简单介绍 Thread类 1 Thread类中一些常用的方法 2 编写一个简单多线程程序 入门 二 Java中创建多线程的方法 重点面试题 1 继承 Thread 类 2 实现 Runnable 接口 重写 run 3 使用匿名内
  • 00.mipi协议

    mipi差分信号原理 理解mipi协议 MIPI DSI LP mode命令及格式详解 MIPI信号的分析 结合示波器实际测试波形 MIPI 移动行业处理器接口 是Mobile Industry Processor Interface的缩写
  • onlyoffice报 error self signed certificate导致download failed错误处理

    安装nextcloud onlyoffice 打开onlyoffice报错 进入容器看out log报错信息 root nextcloud docker ps a Emulate Docker CLI using podman Create
  • HTTP请求详解

    HTTP概念 HTTP全称是HyperText Transfer Protocal 即超文本传输协议 是应用层协议 当你上网浏览网页的时候 浏览器和web服务器之间就会通过HTTP在Internet上进行数据的发送和接收 HTTP是一个基于
  • PHP 两个二维数组求不同

    public function arrcha arr1 0 id gt 1 name gt name arr1 1 id gt 2 name gt name2 arr1 2 id gt 3 name gt name3 arr1 3 id g
  • JNI入门基础

    环境安装 想要使用jni进行ndk开发 我们首先要安装下面这些工具 否则直接从入门到放弃 下载ndk支持 在Android studio中下载上图中框选的两个工具 版本号自己任意选一个 下载完成之后 Android Studio就拥有了进行