安卓使用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 method
    TextView 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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C++笔记之调用PCL库显示PCD文件的点云

C++笔记之调用PCL库显示PCD文件的点云 —— 2024-05-05 杭州 code review! 文章目录 C++笔记之调用PCL库显示PCD文件的点云1.运行2.点云pcd文件github下载地址2.main.cpp3.CMakeLists.txt1.运行 2.点云pcd文件github下载地址 https://github.com/luolaihua/point-cloud-data-…

如果insightface/instantID安装失败怎么办(关于InsightFaceLoader_Zho节点的报错)

可能性有很多&#xff0c;但是今天帮朋友解决问题的时候又收集了一种新的思路。 首先&#xff0c;可以先按照这篇文章里边提到的方法去安装&#xff1a; 【全网最详细】ComfyUI下&#xff0c;Insightface安装指南-聚梦小课堂_insightface如何安装-CSDN博客 其次&#xff0c;…

牛客周赛 Round 41 C-F

C 小红的循环移位 思路&#xff1a; 一个数是不是四的倍数&#xff0c;只用看最后两位是否能够整除4即可。 #include <bits/stdc.h>using namespace std; const int N 1e6 5; typedef long long ll; typedef pair<ll, ll> pll; typedef array<ll, 3> p3;…

IP规划案例

整个OSPF环境IP基于172.16.0.0/16划分 172.16.0.0/16 先分成2个网段&#xff08;OSPF RIP&#xff09;&#xff0c;借1位172.16.0.0/17 ---OSPF 再按区域划分&#xff08;5个区域&#xff09;&#xff0c;借3位 172.16.0.0/20 ---Area 0 三个环回 MGRE 4个网…

京东工业优选商品详情API接口:解锁高效工业采购新体验

京东工业优选的商品详情API接口&#xff0c;允许开发者通过程序化的方式&#xff0c;快速获取平台上的商品详细信息。这些详细信息包括但不限于商品名称、价格、规格、库存、图片、评价等&#xff0c;为企业提供全方位的商品信息查询服务。 二、API接口的主要功能 实时查询&a…

练习项目后端代码解析注解篇(annotation)

前言 本来想从接口处入手的&#xff0c;但是一下看到接口里几十个方法&#xff0c;眼睛有点抗拒&#xff0c;想想还是先看作者写的自定义注解吧。 项目里有三个自定义注解&#xff1a; 分别是AccessLimit注解、OperationLogger注解、VisitLogger注解 AccessLimit注解 这是一…

Python爬虫教程:入门爬取网页数据

1.遵守法律法规 爬虫在获取网页数据时&#xff0c;需要遵守以下几点&#xff0c;以确保不违反法律法规&#xff1a; 不得侵犯网站的知识产权&#xff1a;爬虫不得未经授权&#xff0c;获取和复制网站的内容&#xff0c;这包括文本、图片、音频、视频等。 不得违反网站的使用条…

PXE远程部署CentOS系统

文章目录 在局域网内搭建PXE服务器PXE 启动组件PXE的优点实验一、搭建PXE服务器&#xff0c;实现远程部署CentOS系统环境准备server关闭防火墙安装组件准备 Linux 内核、初始化镜像文件及PXE引导文件配置启用TFTP 服务配置启动DHCP服务准备CentOS 7 安装源配置启动菜单文件 Cli…

【C 数据结构-动态内存管理】3. 伙伴系统管理动态内存

文章目录 【 1. 伙伴系统的结构设计 】【 2. 分配算法 】【 3. 回收算法 】 伙伴系统 本身是一种动态管理内存的方法&#xff0c;和边界标识法的区别是&#xff1a;使用伙伴系统管理的存储空间&#xff0c;无论是空闲块还是占用块&#xff0c;大小都是 2 的 n 次幂&#xff08;…

AI换脸免费软件Rope中文汉化蓝宝石版本全新UI界面,修复部分已知错误【附下载地址与详细使用教程】

rope蓝宝石版&#xff1a;点击下载 注意&#xff1a;此版本支持N卡、A卡、CPU&#xff0c;且建议使用中高端显卡&#xff0c;系统要求win10及以上。 Rope-蓝宝石 更新内容&#xff1a; 0214版更新&#xff1a; ①&#xff08;已修复&#xff09;恢复到以前的模型荷载参数。有…

GDPU 天码行空11

&#xff08;一&#xff09;实验目的 1、掌握JAVA中IO中各种类及其构造方法&#xff1b; 2、重点掌握IO中类所具有的IO操作方法&#xff1b; 3、熟悉软件中登录模块的开发方法&#xff1b; 4、掌握IO中读写常用方法。 5、进一步熟悉正则规则的使用方法。 &#xff08;二&…

初期Linux

一&#xff0c;系统分为 1.1window系统 个人 &#xff1a;win7&#xff0c;win8&#xff0c;Win10&#xff0c;Win11服务器版&#xff1a;window server 2003&#xff0c;window server 2008 1.2Linux系统 centos7redhatubantukali 1.3什么是Linux&#xff1f; Linux是基…

强一致性的皇冠:分布式事务模型的至高法则揭秘

关注微信公众号 “程序员小胖” 每日技术干货&#xff0c;第一时间送达&#xff01; 引言 分布式事务模型是分布式系统设计的核心&#xff0c;关键在于保证数据一致性和事务完整性&#xff0c;尤其强调强一致性。诸如2PC、3PC、Saga、TCC等模型与协议&#xff0c;应运而生以解…

Unity初级---初识生命周期

1. Awake() &#xff1a;唤醒函数&#xff0c;最先执行的函数&#xff0c;只执行一次&#xff0c;当脚本文件挂载的对象被激活时调用 2. OnEnable() &#xff0c;OnDisable()&#xff1a;当脚本启用和禁用时触发&#xff0c;可执行多次&#xff0c;触发的前提是脚本挂载的对象…

QT开发(四) 制作一个JSON检查小工具

1、JSON概念 1.1 定义 JSON&#xff08;JavaScript Object Notation&#xff09;是一种轻量级的数据交换格式&#xff0c;它易于人类阅读和编写&#xff0c;同时也易于机器解析和生成。JSON基于JavaScript语言的子集&#xff0c;但是独立于编程语言&#xff0c;因此可以被多种…

C语言----汉诺塔问题

1.什么是汉诺塔问题 简单来说&#xff0c;就是有三个柱子&#xff0c;分别为A柱&#xff0c;B柱&#xff0c;C柱。其中A柱从上往下存放着从小到大的圆盘&#xff0c;我们需要借助B柱和C柱&#xff0c;将A柱上的所有圆盘转移到C柱上&#xff0c;并且一次只能移动一个圆盘&#…

【Qt QML】Qt Quick Scene Graph

Qt Quick 2是一个用于创建图形界面的库&#xff0c;它使用一个专门的场景图&#xff08;Scene Graph&#xff09;来进行渲染。通过使用OpenGL ES、OpenGL、Vulkan、Metal或Direct 3D等图形API&#xff0c;Qt Quick 2可以有效地优化图形渲染过程。使用场景图而不是传统的命令式绘…

1688工厂货源API接口:用于商品采集、商品搜索、商品详情数据抓取

item_get 获得1688商品详情item_search 按关键字搜索商品item_search_img 按图搜索1688商品&#xff08;拍立淘&#xff09;item_search_suggest 获得搜索词推荐item_fee 获得商品快递费用seller_info 获得店铺详情item_search_shop 获得店铺的所有商品item_password 获得淘口令…

在拥有多个同名称密码的ap环境中,如何连接到指定信道或mac的ap路由器?

在给客户做ESP32-C3入墙开关项目时&#xff0c;客户问&#xff1a;在拥有多个同名称密码的ap环境中&#xff0c;如何连接到指定信道或mac的ap路由器&#xff1f;针对这个问题&#xff0c;启明云端工程师给出下面解决方法。 1、将wifi_sta_config_t配置中的channel配置为该信道…

神经网络怎么把隐含层变量融合到损失函数中?

&#x1f3c6;本文收录于「Bug调优」专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&&…
最新文章