安卓使用so库
最近需要给小伙伴扫盲一下如何使用Android Studio 生成一个SO文件,网上找了很多都没有合适的样例,那只能自己来写一个了。
原先生成SO是一个很麻烦的事情,现在Android Studio帮忙做了很多的事情,基本只要管好自己的C代码即可。
创建工程
C++ Standard :使用下拉列表选择你希望使用哪种 C++ 标准。选择 Toolchain Default 会使用默认的 CMake 设置。
创建后报错的问题
这个是由于我默认使用的 java 1.8 ,需要至少升级到 java11
创建完成后的工程样式
工程解析
native-lib.cpp
这个工程我是这样理解的,native-lib.cpp 是实际编写C++代码的部分,这里来定义方法
#include <jni.h>
#include
#include <android/log.h>
extern “C” JNIEXPORT jstring JNICALL
Java_com_example_myapplication_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
std::string hello = “Hello from C++”;
// 使用 android log输出日志
__android_log_print(ANDROID_LOG_INFO, “log”, “Hello log from JNI function”);
return env->NewStringUTF(hello.c_str());
}
// System.loadLibrary 时发起
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
JNIEnv *env = NULL;
jint result = -1;
__android_log_print(ANDROID_LOG_INFO, "log", "Hello log from JNI_OnLoad");if (jvm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {return -1;
}// 从C 层去调用 java的方法
jclass clazz = env->FindClass("com/example/myapplication/MainActivity");
if (clazz == NULL) {__android_log_print(ANDROID_LOG_ERROR, "log", "Cannot find SampleClass");return -1;
}
jmethodID methodId = env->GetStaticMethodID(clazz, "getJavaString", "()Ljava/lang/String;");
if (methodId == NULL) {__android_log_print(ANDROID_LOG_ERROR, "log", "Cannot find sampleMethod");return -1;
}
/*** 这行代码是在 JNI 中调用 Java 类的静态方法,并将返回值转换为 jstring 类型的对象。env->CallStaticObjectMethod(clazz, methodId) 是 JNI 中用于调用 Java 类的静态方法的函数。它接受三个参数:类引用 clazz、方法 ID methodId 和任何必要的参数列表。在这种情况下,我们调用了一个静态方法,并且没有传递其他参数,因此只传递了类引用 clazz 和方法 ID methodId。静态方法执行后会返回一个 Java 对象,因此我们使用 (jstring) 将其强制转换为 jstring 类型的对象,因为我们知道该方法返回的是字符串对象。最终,返回的 jstring 对象被赋值给名为 resultString 的变量,以便后续在 JNI 函数中进行处理或操作。*/
jstring resultString = (jstring)env->CallStaticObjectMethod(clazz, methodId);
// 从 Java 字符串对象中获取一个指向 UTF-8 编码的 C 字符串的指针,并将其存储在名为 str 的 const char* 类型指针变量中。
const char* str = env->GetStringUTFChars(resultString, NULL);
__android_log_print(ANDROID_LOG_INFO, "log", "Output from Java: %s", str);
// 释放由 GetStringUTFChars 函数获取的 UTF 字符串的内存
env->ReleaseStringUTFChars(resultString, str);result = JNI_VERSION_1_6;
return result;
}
extern “C” JNIEXPORT jstring JNICALL
在 JNI(Java Native Interface)中,extern “C” 用于指定 C++ 函数按照 C 语言的命名和调用约定来处理。JNIEXPORT 和 JNICALL 是 JNI 提供的宏,通常用于声明 JNI 函数,这两个宏通常会展开为适合当前环境的修饰符。
extern “C” 告诉编译器按照 C 语言的规则处理函数 stringFromJNI。
JNIEXPORT 表示该函数将被导出供 JNI 调用。
JNICALL 是一个宏,用于设置正确的调用约定。
include
上面 include 就是C当中引入相关库的地方。
这里我加了 #include <android/log.h> 用于使用Android log 方法:__android_log_print
这里不可以直接使用C原生的 printf(“Hello log from JNI function\n”)
因为在 Android 开发中,printf 输出的内容通常不会直接显示在 Logcat 中。Android 应用默认会将 stdout 和 stderr 重定向到 /dev/null,因此 printf 的输出不会在 Logcat 中出现
Java_com_example_myapplication_MainActivity_stringFromJNI
Java: 这个部分表示这是一个 JNI 函数的标识符,表明该函数是与 Java 代码进行交互的本机方法。
com_example_myapplication_MainActivity: 这部分指定了 Java 类的完整路径,即 com.example.myapplication.MainActivity。这个路径应该与包名和类名一致,使用下划线 _ 替代点号 .。
stringFromJNI: 这部分是 Java 类中方法的名称,这个名称应该与 Java 类中定义的native方法名称一致。也就是 :public native String stringFromJNI();
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.22.1)
Declares and names the project.
project(“myapplication”)
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.
myapplication
# 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.
myapplication
# Links the target library to the log library# included in the NDK.${log-lib})
这里逐条解析
cmake_minimum_required(VERSION 3.22.1)
这个指定了构建该项目所需的最低 CMake 版本为 3.22.1、
project(“myapplication”)
声明并命名项目为 “myapplication”。
add_library
创建并命名一个库,设置为 SHARED 类型,并提供源代码文件的相对路径。在此示例中,创建了名为 myapplication 的共享库,并提供了 native-lib.cpp 源文件的路径。
find_library
在系统路径中搜索指定的预构建库,并将其路径存储为变量。在这里,查找名为 log 的 NDK 库,并将路径存储在 log-lib 变量中。
target_link_libraries
指定要链接到目标库的库。在这里,将 myapplication 目标库链接到 log NDK 提供的 log 库。
所以当你需要改变生成的so的名称时,需要改动 add_library中的myapplication以及关联target_link_libraries中的值,不然报错。
同时,可以看到 find_library 作用是引入log库,如果不使用log,那么这个就并不是必须的,如果我在native-lib.cpp 中不使用那句 __android_log_print ,那么就可以精简为:
cmake_minimum_required(VERSION 3.22.1)
project(“myapplication”)
add_library(myapplication SHARED native-lib.cpp)
MainActivity
package com.example.myapplication
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.example.myapplication.databinding.ActivityMainBinding
public class MainActivity extends AppCompatActivity {
// Used to load the 'myapplication' library on application startup.
static {System.loadLibrary("myapplication");
}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 methodTextView tv = binding.sampleText;tv.setText(stringFromJNI());
}public static String getJavaString(){return "javaString 123";
}/*** A native method that is implemented by the 'myapplication' native library,* which is packaged with this application.*/
public native String stringFromJNI();
}
这个其实没什么可以多说的,就是标准的调用,这里可以测试调用需要的代码。同时,因为so对 class的包名以及方法名固定的特性,使用so的地方也需要这里的代码。
SO编译
如果只是测试,可以直接run apk 。对应的文件会生成在如下位置。
此时,apk中只会包含和你测试设备相符合的so架构
但是当你需要多个架构时,需要在 build.gradle 中进行指定
然后直接编正式APK即可。
编完之后再apk 中可以获取到你需要的架构so
SO安全
这里额外聊一下这个事情,很多人觉得代码放在SO里面,别人不好反编译,更加的安全。
但是有一个盲点,就是别人在看完你的 Android 代码之后,读完你 native定义,可以直接使用你的so来进行操作。
例如你把关键的加密函数做成SO,明文 -> so -> 密文,或者 密文 -> so -> 明文,那别人直接调用你的so就解密了。特别是使用加固的项目,so很多都不加固,只加固java,这样反而不安全。
所以so至少要进行签名校验。当然so的校验也借助 java 的packagemanage,也就是容易被上层hook,这个我们有额外的对抗方案,这里不细说。
放一个so获取签名的代码在这里。具体的so代码后面有机会再开新坑
extern “C” JNIEXPORT jstring JNICALL
Java_com_example_myapplication_MainActivity_getAppSignature(JNIEnv *env,jobject obj) {
jclass context_class = env->FindClass(“android/content/Context”);
jmethodID method_getPackageManager = env->GetMethodID(context_class, “getPackageManager”,
“()Landroid/content/pm/PackageManager;”);
jmethodID method_getPackageName = env->GetMethodID(context_class, “getPackageName”,
“()Ljava/lang/String;”);
jobject context = obj;
jobject package_manager = env->CallObjectMethod(context, method_getPackageManager);
jstring package_name = static_cast<jstring>(env->CallObjectMethod(context,method_getPackageName));jclass package_manager_class = env->GetObjectClass(package_manager);
jmethodID method_getPackageInfo = env->GetMethodID(package_manager_class, "getPackageInfo","(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");// 获取签名信息
jobject package_info = env->CallObjectMethod(package_manager, method_getPackageInfo, package_name, 64);
jclass package_info_class = env->GetObjectClass(package_info);
jfieldID field_signatures = env->GetFieldID(package_info_class, "signatures","[Landroid/content/pm/Signature;");
jobjectArray signatures = (jobjectArray) env->GetObjectField(package_info, field_signatures);
jobject signature = env->GetObjectArrayElement(signatures, 0);jclass signature_class = env->GetObjectClass(signature);
jmethodID method_toCharsString = env->GetMethodID(signature_class, "toCharsString","()Ljava/lang/String;");
jstring signature_str = (jstring) env->CallObjectMethod(signature, method_toCharsString);return signature_str;
原文链接:https://blog.csdn.net/vistaup/article/details/136650838
相关文章:
安卓使用so库
最近需要给小伙伴扫盲一下如何使用Android Studio 生成一个SO文件,网上找了很多都没有合适的样例,那只能自己来写一个了。 原先生成SO是一个很麻烦的事情,现在Android Studio帮忙做了很多的事情,基本只要管好自己的C代码即可。 …...
【介绍下LeetCode的使用方法】
🌈个人主页: 程序员不想敲代码啊 🏆CSDN优质创作者,CSDN实力新星,CSDN博客专家 👍点赞⭐评论⭐收藏 🤝希望本文对您有所裨益,如有不足之处,欢迎在评论区提出指正,让我们共…...
重学java 30.API 1.String字符串
于是,虚度的光阴换来了模糊 —— 24.5.8 一、String基础知识以及创建 1.String介绍 1.概述 String类代表字符串 2.特点 a.Java程序中的所有字符串字面值(如“abc”)都作为此类的实例(对象)实现 凡是带双引号的,都是String的对象 String s "abc&q…...
【区块链】共识算法简介
共识算法简介 区块链三要素: 去中心化共识算法智能合约 共识算法作为区块链三大核心技术之一,其重要性不言而喻。今天就来简单介绍共识算法的基本知识。 最简单的解释,共识算法就是要让所有节点达成共识,保证少数服从多数&#x…...
Qt---day2-信号与槽
1、思维导图 2、 拖拽式 源文件 #include "mywidget.h" #include "ui_mywidget.h" MyWidget::MyWidget(QWidget *parent) : QWidget(parent) , ui(new Ui::MyWidget) { ui->setupUi(this); //按钮2 this->btn2new QPushButton("按钮2",th…...
Python中设计注册登录代码
import hashlib import json import os import sys # user interface 用户是界面 UI """ 用户登录系统 1.注册 2.登陆 0.退出 """ # 读取users.bin def load(path): return json.load(open(path, "rt")) # 保存user.bin def save(dic…...
AI伦理和安全风险管理终极指南
人工智能(AI)正在迅速改变各个领域的软件开发和部署。驱动这一转变的两个关键群体为人工智能开发者和人工智能集成商。开发人员处于创建基础人工智能技术的最前沿,包括生成式人工智能(GenAI)模型、自然语言处理&#x…...
golang testing使用
testing包服务于自动化测试 基本测试 Table Drvien Test 基于表的测试通过表形式进行测试每种情况的输入和期望输出,从而测试程序的正确性 func TestFib(t *testing.T) {var fibTests []struct {in int // inputexpected int // expected result}{{1, 1}…...
在Excel中使用正则提取单元格内容
在办公自动化的浪潮中,Excel 作为数据处理的利器,一直在不断进化。最近,我注意到了不坑盒子Office插件一个非常实用的功能更新——bk_regex_string 公式。这个功能对于我们这些日常需要处理大量文本和数据的办公人员来说,无疑是一…...
SQL查询语句(二)逻辑运算关键字
上一篇文章中我们提到了条件查询除了一些简单的数学符号之外,还有一些用于条件判断的关键字,如逻辑判断 关键字AND,OR,NOT和范围查找关键字BETWEEN,IN等;下面我们来介绍一些这些关键字的用法以及他们所表达的含义。 目录 逻辑运算关键字 AND…...
矿山机械自动化中的激光雷达技术探索
在矿山机械自动化技术的快速发展中,激光雷达技术作为其关键组成部分,正发挥着越来越重要的作用。本文将深入探讨激光雷达在矿山机械自动化中的应用,以及其所面临的挑战与未来发展趋势。 一、激光雷达在矿山机械自动化中的应用 激光雷达技术…...
MOSFET场效应管栅极驱动电流的计算
MOSFET驱动 MOSFET场效应管是电压驱动器件,输入有电容,因此为可靠驱动MOSFET,栅极需要施加较大的驱动电流。 功率MOSFET开关模型 该模型显示了影响开关性能的最重要的寄生器件。 栅极所需驱动电流计算公式 一个很重要的参数是计算栅极驱…...
Python 爬虫:Spring Boot 反爬虫的成功案例
前言 在当今数字化时代,网络数据成为了信息获取和分析的重要来源之一。然而,随着网络数据的广泛应用,爬虫技术也逐渐成为了互联网行业的热门话题。爬虫技术的应用不仅可以帮助企业获取有价值的信息,还可以用于数据分析、市场研究…...
计算机毕业设计Python+Vue.js天气预测系统 中国气象质量采集与可视化 天气数据分析 天气可视化 天气大数据 天气爬虫 大数据毕业设计
摘要 随着科技技术的不断发展,人民物质生活质量不断提高,我们越来越关注身边的气象、空气等地理环境。对于普通居民我们会选择合适的气象进行出游,提高精神层面的生活质量;对于企业会关注气象变换状况,来定制相关的生产…...
【busybox记录】【shell指令】tr
目录 内容来源: 【GUN】【tr】指令介绍 【busybox】【tr】指令介绍 【linux】【tr】指令介绍 使用示例: 转换字符 - 默认 转换字符 - 不翻译指定字符数组 此指令目前接触少,用得少,把精力放到其他常用指令上 常用组合指令…...
Mac虚拟机软件哪个好用 mac虚拟机parallels desktop有什么用 Mac装虚拟机的利与弊 mac装虚拟机对电脑有损害吗
随着多系统使用需求的升温,虚拟机的使用也变得越来越普遍。虚拟机可以用于创建各种不同的系统,并按照要求设定所需的系统环境。另外,虚拟机在Mac电脑的跨系统使用以及测试软件系统兼容性等领域应用也越来越广泛。 一、Mac系统和虚拟机的区别 …...
Type-C转音频(USB2.0数据传输)+PD充电芯片乐得瑞LDR6500/LDR6023
LDR6500 USB-C DRP 接口 USB PD 通信芯片概述 Type-C转音频(USB2.0数据传输)PD充电芯片乐得瑞LDR6500LDR6500是乐得瑞科技针对USB Type-C标准中的Bridge设备而开发的USB-C DRP(Dual Role Port,双角色端口)接口USB PD(Power Deliv…...
【busybox记录】【shell指令】expand
目录 内容来源: 【GUN】【expand】指令介绍 【busybox】【expand】指令介绍 【linux】【expand】指令介绍 使用示例: 把制表符转化为空格 - 默认输出 把制表符转化为空格 - 修改制表符转空格的个数 把制表符转化为空格 - 修改制表符转空格的个数…...
软件测试—— 接口测试之通讯流程相关概念
通讯流程 1、协议 通讯规则 2、HTTP协议 协议的一种 3、接口规范文档 如何发请求的要求文档,获取什么响应内容的说明文档(相当于菜单)...
AT32 雅特力CAN详细使用说明配置细则
CAN 过滤器使用说明 CAN 过滤器相当于关卡,每当收到一条报文时,CAN 要先将收到的报文从这些过滤器上"过滤"一下,能通 过的报文是有效报文,收进相关联 FIFO(FIFO0 或 FIFO1),不能通过的…...
Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务
通过akshare库,获取股票数据,并生成TabPFN这个模型 可以识别、处理的格式,写一个完整的预处理示例,并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务,进行预测并输…...
linux 错误码总结
1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...
【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验
系列回顾: 在上一篇中,我们成功地为应用集成了数据库,并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了!但是,如果你仔细审视那些 API,会发现它们还很“粗糙”:有…...
RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill
视觉语言模型(Vision-Language Models, VLMs),为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展,机器人仍难以胜任复杂的长时程任务(如家具装配),主要受限于人…...
脑机新手指南(七):OpenBCI_GUI:从环境搭建到数据可视化(上)
一、OpenBCI_GUI 项目概述 (一)项目背景与目标 OpenBCI 是一个开源的脑电信号采集硬件平台,其配套的 OpenBCI_GUI 则是专为该硬件设计的图形化界面工具。对于研究人员、开发者和学生而言,首次接触 OpenBCI 设备时,往…...
Spring AI Chat Memory 实战指南:Local 与 JDBC 存储集成
一个面向 Java 开发者的 Sring-Ai 示例工程项目,该项目是一个 Spring AI 快速入门的样例工程项目,旨在通过一些小的案例展示 Spring AI 框架的核心功能和使用方法。 项目采用模块化设计,每个模块都专注于特定的功能领域,便于学习和…...
深入浅出Diffusion模型:从原理到实践的全方位教程
I. 引言:生成式AI的黎明 – Diffusion模型是什么? 近年来,生成式人工智能(Generative AI)领域取得了爆炸性的进展,模型能够根据简单的文本提示创作出逼真的图像、连贯的文本,乃至更多令人惊叹的…...
Docker拉取MySQL后数据库连接失败的解决方案
在使用Docker部署MySQL时,拉取并启动容器后,有时可能会遇到数据库连接失败的问题。这种问题可能由多种原因导致,包括配置错误、网络设置问题、权限问题等。本文将分析可能的原因,并提供解决方案。 一、确认MySQL容器的运行状态 …...
rknn toolkit2搭建和推理
安装Miniconda Miniconda - Anaconda Miniconda 选择一个 新的 版本 ,不用和RKNN的python版本保持一致 使用 ./xxx.sh进行安装 下面配置一下载源 # 清华大学源(最常用) conda config --add channels https://mirrors.tuna.tsinghua.edu.cn…...
spring Security对RBAC及其ABAC的支持使用
RBAC (基于角色的访问控制) RBAC (Role-Based Access Control) 是 Spring Security 中最常用的权限模型,它将权限分配给角色,再将角色分配给用户。 RBAC 核心实现 1. 数据库设计 users roles permissions ------- ------…...
