Android NDK开发基础

news2024/9/21 20:47:58

文章目录

      • cmake语法基础
        • cmake添加日志:
        • cmake增加宏
        • 字符串比较
        • cmake在build.gradle中传递编译参数到cmake
      • 通过javah生成native对应的c++头文件
      • jni和java之间字符串的相互操作
      • JavaVM和JNIEnv
      • 字符串的编码
      • native方法静态注册和动态注册
        • 静态注册
        • 动态注册
      • extern c
      • C++中STATIC和SHARE库类型的区别
      • c++控制so导出的函数符号的可见性
      • jni处理异常
      • NDK工具使用:

根据日常学习持续更新中

cmake语法基础

cmake添加日志:

message([] “message text” …)

Record the specified message text in the log. If more than one message string is given, they are concatenated into a single message with no separator between the strings.

mode参数可以有不同的选项,一般不会选择ERROR级别,ERROR会停止cmake运行

  • WARNING: CMake Warning, continue processing.
  • 还有其他很多mode可以参考下面的cmake_message

如果要在日志中打印变量的值的话可以使用${}在引号中包裹变量

message(CHECK_FAIL “missing components: ${variable}”)

参考:cmake_message

cmake增加宏

add_definitions(-DDEBUG) 是定义宏-D后面是宏的名称,在c++代码中我们可以使用ifdef DEBUG 来使用我们的编译参数

字符串比较

if ("${variable}" STREQUAL "true"){
}else{
}

获取编译参数重传递到cmake的值,然后比较字符串然后进行判断

cmake在build.gradle中传递编译参数到cmake

 //cmake 的参数配置入口
         externalNativeBuild {
            cmake {
                  // 指定一些编译选项
                cppFlags "-std=c++11 -frtti -fexceptions"
                //如何向变量传递参数,对应的格式如下(arguments "-D变量名=参数")
                arguments '-DANDROID_PLATFORM=android-24', '-DANDROID_STL=c++_static', '-DANDROID_STL=c++_shared'
               // 也可以使用下面这种语法向每个变量传递多个参数(参数之间使用空格隔开),格式如下
               // arguments "-D变量名=参数1 参数2"
                arguments  "-DANDROID_CPP_FEATURES=rtti exceptions"

            }
        }

参考:
https://blog.csdn.net/ljx1400052550/article/details/117280541

通过javah生成native对应的c++头文件

在方法参数前添加native关键字
例如:

    public native String get();

javah 输入命令的目录需要是包名的根目录,也就是需要包含包名
终端路径:/Users/lxd/code/Android/lxdAndroidStart/app/src/main/java
命令:javah com.example.androidstart.JniTest

jni和java之间字符串的相互操作

    const char *str = env->GetStringUTFChars(jstr, JNI_FALSE);
    int len = env->GetStringUTFLength(jstr);
    printf("from java str=%s, len=%d", str, len);
    env->ReleaseStringUTFChars(jstr, str);

在这里插入图片描述

    const char *str = "hello, world";
    return env->NewStringUTF(str);

在这里插入图片描述
方法签名生成:
javap
-s 输出内部类型签名
传入-s后面的参数需要是classes,可以通过javac获取
javac 编译java文件生成class文件/或者可以去项目编译中的中间产物中去寻找class文件
c++ lambda
Lambda表达式完整的声明格式如下:

[capture list] (params list) mutable exception-> return type { function body }

  • capture list:捕获外部变量列表
  • params list:形参列表
  • mutable指示符:用来说用是否可以修改捕获的变量
  • exception:异常设定
  • return type:返回类型
  • function body:函数体

链接:
https://www.cnblogs.com/DswCnblog/p/5629165.html

JavaVM和JNIEnv

JavaVM
JavaVM再Android中只有一个,JavaVM带有函数表,允许你创建和销毁JavaVM。
JNIEnv
JNIEnv提供了大多数的JNI函数,对于C语言的代码,本地函数都需要接收JNIEnv为第一个参数,而对于C++,JNIEnv不需要作为参数传入
JNIEnv用做线程私有存储,因此,不能在线程间共享JNIEnv变量,如果一个代码块没有JNIEnv,可以通过JavaVM去获取
在jni.h的定义中,针对c++和c的不同,有着不同的定义,因此两种语言混用的时候需要注意。
在这里插入图片描述

字符串的编码

java中字符串使用的是UTF-16编码,
JNI中使用 utf-8 表示字符串,UTF-8是变长编码的unicode,一般ascii字符是1字节,中文是3字节;
c/c++使用的是原始数据,ascii就是一个字节了,中文一般是GB2312编码,用两个字节来表示一个汉字。
所以三种类型的字符串如果含有中文的时候需要特殊转换下

native方法静态注册和动态注册

首先我们在java中使用native关键字声明这个方法是native方法,然后使用静态注册或者动态注册,将native方法和c++实现绑定

public native void nativeStaticRegister();

静态注册

生成native方法对应的c++头文件
使用javah生成class文件对应的头文件,-d 第一个参数是输出路径,第二个参数是src目录下的类的全名
在对应的Terminal路径输入命令,我的路径是这个/Users/XXX/code/Android/NativeJni/app/src/main/java

javah -d …/cpp/ com.example.nativejni.CallBackClass

输入了上面的命令后就会在 cpp 目录下生成对应的cpp头文件
直接cpp文件中输入native方法名,as会提示回车后自动补全

或者我们将公共部分提出来,写成一个宏,然后使用宏

#define FFMPEG_FUNC(RETURN_TYPE, FUNC_NAME, ...) \
    JNIEXPORT RETURN_TYPE JNICALL Java_com_example_nativejni_MainActivity_##FUNC_NAME \
    (JNIEnv *env, jclass thiz, ##__VA_ARGS__)

动态注册

动态注册我们在JNI_OnLoad方法中使用RegisterNatives进行注册,将java的native方法和c++进行绑定。
因为绑定的时候需要字节码的方法签名:
获取方法签名的方式

extern c

c++中jni的方法前都有个这个关键字,
不带extern c编译出来的so中的符号

_Z46Java_com_example_nativejni_MainActivity_getvalP7_JNIEnvP8_jobject

带extern c

Java_com_example_nativejni_MainActivity_getval

带extern c编译出来的符号才符合jni命名,extern c让编译器使用c的编译规则编译指定代码
查看so中符号的方法:
在我们的ndk目录下,比如我的路径是

ndk/21.4.7075529/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64/bin/
aarch64-linux-android-nm

在这个terminal执行 ./aarch64-linux-android-nm so路径
就会展示出so的符号列表(对于debug包apk中解压出来的so自己试了下需要加上-D参数才能显示动态链接符号)

./aarch64-linux-android-nm --help 查看-D参数的含义
-D, --dynamic Display dynamic symbols instead of normal symbols

参考:https://blog.csdn.net/sinat_36817189/article/details/110423243

C++中STATIC和SHARE库类型的区别

STATIC静态库:变异的时候会将程序和静态库进行链接,可执行程序中会包含当前的静态库,多个可执行程序会有多份静态库。
SHARED动态库:动态库的调用和链接是在运行时,可执行程序中并不包含动态库,多个可执行程序共享一份动态库

c++控制so导出的函数符号的可见性

  1. 当-fvisibility=hidden时动态库中的函数默认是被隐藏的即 hidden. 除非显示声明为__attribute__((visibility("default"))).

  2. 当-fvisibility=default时动态库中的函数默认是可见的.除非显示声明为__attribute__((visibility("hidden")))

jni处理异常

    if (env->ExceptionOccurred()) {
        LOGI("occurred lxd exception");
        env->ExceptionClear();
    }

可以使用
DirectBuffer实现native和c++层的更高效的数据传递,但是堆外内存的创建和销毁比较耗时。

链接:

java JNI官方教程

NDK工具使用:

ndk-stask查看崩溃堆栈

$NDK/ndk-stack -sym $PROJECT_PATH/obj/local/armeabi-v7a -dump foo.txt

上面的foo.txt指的是崩溃的堆栈,可以从崩溃的日志中拷贝出来,要从*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***开始拷贝,要包含这个

 ./ndk-stack --help
usage: ndk-stack.py [-h] -sym SYMBOL_DIR [-i INPUT]

Symbolizes Android crashes.

optional arguments:
  -h, --help            show this help message and exit
  -sym SYMBOL_DIR, --sym SYMBOL_DIR
                        directory containing unstripped .so files
  -i INPUT, -dump INPUT, --dump INPUT
                        input filename

See <https://developer.android.com/ndk/guides/ndk-stack>.

上面的-sym传入的SYMBOL_DIR要求是unstripped,unstripped是啥意思呢

在我们编译so生成的产物下面,cmake的产物没有strip,so会大很多
在这里插入图片描述
striped目录下面会有去处符号的so,体积会小很多
在这里插入图片描述
/Users/lxd/Library/Android/sdk/ndk/21.4.7075529/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64/bin
strip工具目录

链接:
官方ndk-stack使用教程
https://blog.csdn.net/yangzex/article/details/126581161

addr2line查看代码位置
// 0x12345678为堆栈地址,替换为实际崩溃地址我们可以查看到我们的代码崩溃的位置

aarch64-linux-android-addr2line -e libxxx.so 0x12345678

readelf -d libxxx.so查看其依赖库:

./aarch64-linux-android-readelf --help
-d --dynamic Display the dynamic section (if present)

objdump 反汇编so文件

./arm-linux-androideabi-objdump –S libxx.so

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1875.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringCloud Alibaba-Sentinel保姆级教程

文章目录1、Sentinel前奏1.1、服务雪崩效应1.2、常见容错方案1、隔离2、超时3、限流4、熔断5、降级1.3、常见容错组件1、Hystrix2、Resilience4J3、Sentinel2、Sentinel入门2.1、什么是Sentinel2.2、实战编码1、安装Sentinel服务端2、微服务引入Sentinel2.3、Sentinel流控模式1…

YOLOv5蒸馏 | 知识蒸馏理论篇 | 1/2

之前在《一文搞懂【知识蒸馏】【Knowledge Distillation】算法原理》这篇文章中介绍过一些知识蒸馏的原理,这篇博文将会着重介绍目标检测领域的知识蒸馏原理。 文章目录 1.《Object detection at 200 Frames Per Second》1.1 蒸馏难点1.2 蒸馏损失1.3 实验结果2. 《Learning E…

计算机毕业设计ssm+vue基本微信小程序的体检预约小程序

项目介绍 我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;体检预约系统小程序被用户普遍使用&#xff0c;为方便用户…

【前端】CSS(1) —— CSS的基本语法和一些简单的选择器

JavaEE传送门JavaEE 网络原理——网络层与数据链路层 【前端】HTML入门 —— HTML的常见标签 目录CSS基本语法注释引入方式内部样式内联样式外部样式代码风格样式格式样式大小写空格规范CSS 选择器标签选择器类选择器id 选择器通配符选择器复合选择器后代选择器子选择器并集选…

文件包含漏洞和hash破解

介绍 Windows是当今世界最主要的操作系统&#xff0c;因为它易于使用的GUI可访问性。大约85%的市场份额已成为攻击的关键操作系统。此外&#xff0c;大多数组织使用Active Directory来设置其Windows域网络。微软聘请NTLM&#xff08;New Technology LAN Manager&#xff09;和…

Pico Neo3 4VR游戏下载地址及十大好玩游戏推荐

大家好&#xff0c;杰克今天为大家分享的是VR一体机游戏。 说到VR一体机&#xff0c;我们首先想到的一定是国产之光pico啦&#xff0c;picovr被认为是国内最被看好的VR一体机&#xff0c;有望在国内取代红极一时的OculusQuest&#xff0c;填补这块市场的空白。OculusQuest将6D…

#Primavera Unifier:关于零代码/低代码平台特点【2/3】

在之前对Unifier的介绍中&#xff0c;我提到了Unifier应用的一个非常关键的特征&#xff0c;及零代码快速配置使用&#xff0c;而为了更好的介绍Oracle Primavera Unifier 的零代码特点&#xff0c;以下我将通过3篇内容来逐一介绍零代码/低代码平台的特点。 前面介绍到了零代码…

Opencv项目实战:14 手势控制音量

目录 0、项目介绍 1、项目展示 2、项目搭建 3、项目的代码与讲解 4、项目资源 5、项目总结 0、项目介绍 本篇与上一篇有很多联系&#xff0c;大家可以看看这篇Opencv项目实战&#xff1a;13 手部追踪&#xff0c;我们将根据上一节的内容&#xff0c;进行一个拓展。本篇你…

AtCoder Beginner Contest 275 【E】【F】

E - Sugoroku 4 【概率dp】 题意&#xff1a; 对于每个样例&#xff0c;读入n&#xff0c;m&#xff0c;k。 一维数轴&#xff0c;你现在在0这个点上&#xff0c;目标是到达n这个点&#xff0c;你有k次掷骰子的机会&#xff0c;每次可能等概率的掷出1~m的任意一个数字&#xff…

面了一个4年经验的测试工程师,自动化都不会也要15k,我真是醉了...

在深圳这家金融公司也待了几年&#xff0c;被别人面试过也面试过别人&#xff0c;大大小小的事情也见识不少&#xff0c;今天又是团面的一天&#xff0c; 一百多个人都聚集在一起&#xff0c;因为公司最近在谈项目出来面试就2个人&#xff0c;无奈又被叫到面试房间。 整个过程…

.NET Core HttpReports 监控

HttpReports 基于.NET Core 开发的APM监控系统&#xff0c;使用MIT开源协议&#xff0c;主要功能包括&#xff0c;统计, 分析, 可视化&#xff0c; 监控&#xff0c;追踪等&#xff0c;适合在中小项目中使用。 语雀&#xff1a;https://www.yuque.com/httpreports/docs/uyaiil …

SQL注入之绕过is_numeric过滤

目录预备知识PHP常用的过滤类函数is_numeric()函数介绍实验目的实验环境实验步骤一通过源代码审计&#xff0c;发现临时文件实验步骤二通过分析源代码的执行逻辑&#xff0c;了解程序的功能&#xff0c;发现程序中的安全漏洞实验步骤三绕过过滤函数实现SQL注入预备知识 PHP常用…

Linux进程信号

文章目录什么是信号signal函数的功能&#xff08;捕捉信号后自己处理&#xff09;Core Dump&#xff08;核心转储&#xff09;kill&#xff0c;raise&#xff0c;alarm系统调用再度理解OS给进程发送信号信号集操作函数自定义捕捉详解什么是信号 生活中的信号&#xff1a;闹钟&…

0083 环形链表

package LinkedList_; /* * 单向环形链表应用场景——约瑟夫问题 * 设编号为1&#xff0c;2....n的n个人围成一圈&#xff0c;约定编号为k&#xff08;1<k<n&#xff09;的人从1开始报数&#xff0c;数到m的人出列&#xff0c; * 它的下一位又从…

黑胶歌曲没权限,还好我会Python,一分钟一个歌单,硬盘有点不够用了~

今日份Python白嫖人生苦短&#xff0c;我用Python一、你需要准备1、环境2、模块二、效果展示三、代码展示四、写在最后人生苦短&#xff0c;我用Python 人之初&#xff0c;喜白嫖。大家都喜欢白嫖&#xff0c;我也喜欢&#xff0c;那么今天就来试试怎么白嫖抑云~ 我不是&#…

React面向组件编程(定义组件,组件三大核心属性,组件事件处理、组件收集表单数据、高阶函数和函数的柯里化)

目录 一、React中定义组件 1、函数式组件 2、类式组件 二、组件三大核心属性 1、组件三大核心属性1: State(状态) 2、组件三大核心属性2: props 3、组件三大核心属性3: ref 三、组件事件处理 1、事件处理 四、组件收集表单数据 1、受控组件 2、非受控组件 五、高阶函…

【数据结构】算法的时间复杂度和空间复杂度

&#x1f680; 作者简介&#xff1a;一名在后端领域学习&#xff0c;并渴望能够学有所成的追梦人。 &#x1f40c; 个人主页&#xff1a;蜗牛牛啊 &#x1f525; 系列专栏&#xff1a;&#x1f6f9;初出茅庐C语言、&#x1f6f4;数据结构 ☀️ 学习格言&#xff1a;眼泪终究流不…

象棋中的马跳步问题

象棋中的马跳步问题 作者&#xff1a;Grey 原文地址&#xff1a; 博客园&#xff1a;象棋中的马跳步问题 CSDN&#xff1a;象棋中的马跳步问题 题目描述 中国象棋中&#xff0c;整个棋盘就是横坐标上 9 条线、纵坐标上 10 条线的一个区域&#xff0c;给你三个 参数 x&…

计算机毕业设计springboot+vue基本微信小程序的汽车租赁公司小程序

项目介绍 随着信息技术和网络技术的飞速发展&#xff0c;人类已进入全新信息化时代&#xff0c;传统管理技术已无法高效&#xff0c;便捷地管理信息。为了迎合时代需求&#xff0c;优化管理效率&#xff0c;各种各样的管理系统应运而生&#xff0c;各行各业相继进入信息管理时…

【Vue】环境搭建

Vue 简介&#xff1a; 一套用于构建用户界面的渐进式框架。与其它大型框架不同的是&#xff0c;Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层&#xff0c;不仅易于上手&#xff0c;还便于与第三方库或既有项目整合。另一方面&#xff0c;当与现代化的工具链以…