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

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

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

相关文章

QT creator qt6.0 使用msvc2019 64bit编译报错

qt creator qt6.0报错&#xff1a; D:\Qt6\6.3.0\msvc2019_64\include\QtCore\qglobal.h:123: error: C1189: #error: "Qt requires a C17 compiler, and a suitable value for __cplusplus. On MSVC, you must pass the /Zc:__cplusplus option to the compiler."…

VTK —— 三、简单操作 - 示例3 - 将点投影到平面上(附完整源码)

代码效果 本代码编译运行均在如下链接文章生成的库执行成功&#xff0c;若无VTK库则请先参考如下链接编译vtk源码&#xff1a; VTK —— 一、Windows10下编译VTK源码&#xff0c;并用Vs2017代码测试&#xff08;附编译流程、附编译好的库、vtk测试源码&#xff09; 教程描述 本…

什么是视频号小店?为什么这么多人都在做?一文带你轻松入门!

大家好&#xff0c;我是电商花花。 现在电商的快速发展&#xff0c;电商行业在各大电商平台上不断发展&#xff0c;而视频号小店也被更多人看到和入驻&#xff0c;让更多创业者对视频号小店产生兴趣。 知道的人都觉得视频号小店是一个不可多得的创业项目&#xff0c;因为这里…

rust 使用记录

rust 使用记录 安装包管理镜像源 安装 参考 rust install sudo apt install -y curl # 安装 curl --proto https --tlsv1.2 -sSf https://sh.rustup.rs | sh # 加载环境变量 source $HOME/.cargo/env # 查看版本 rustc --version rustc -V# 更新Rust rustup update# 卸载 # r…

下一代Nginx? OpenNjet 的入门实践

何为 OpenNjet &#xff1f; OpenNJet 应用引擎是基于 NGINX 的面向互联网和云原生应用提供的运行时组态服务程序&#xff0c;作为底层引擎&#xff0c;OpenNJet 实现了NGINX 云原生功能增强、安全加固和代码重构&#xff0c;利用动态加载机制可以实现不同的产品形态&#xff0…

休斯《公共管理导论》第5版/考研真题解析/章节题库

第一部分 考研真题精选 一、概念题二、简答题三、论述题四、案例分析题第二部分 章节题库 第1章 一个变革的时代第2章 政府的角色第3章 传统的公共行政模式第4章 公共管理第5章 公共政策第6章 治 理第7章 问 责第8章 利害关系人和外部环境第9章 管制、外包和公共企…

【机器学习与实现】线性回归示例——波士顿房价分析

目录 一、创建Pandas对象并查看数据的基本情况二、使用皮尔逊相关系数分析特征之间的相关性三、可视化不同特征与因变量MEDV&#xff08;房价中值&#xff09;间的相关性四、划分训练集和测试集并进行回归分析 一、创建Pandas对象并查看数据的基本情况 boston.csv数据集下载&a…

Go 语言基础之面向对象编程

1、OOP 首先&#xff0c;Go 语言并不是面向对象的语言&#xff0c;只是可以通过一些方法来模拟面向对象。 1.1、封装 Go 语言是通过结构体&#xff08;struct&#xff09;来实现封装的。 1.2、继承 继承主要由下面这三种方式实现&#xff1a; 1.2.1、嵌套匿名字段 //Add…

CoPilot 产品体验:提升 OpenNJet 的控制管理和服务提供能力

文章目录 前言系统架构介绍CoPilot 配置CoPilot 插件规范 体验 CoPilot 实例CoPilot: Broker 实例CoPilot: Ctrl 实例 开发其他语言编写的 CoPilot目标主要思路具体实现执行 go 程序代码 功能扩展总结 前言 CoPilot 是 OpenNJet 的一个重要组成部分&#xff0c;它在 Master-Wo…

246 基于matlab的交流电机动态方程

基于matlab的交流电机动态方程&#xff0c;用于交流电机动态分析。输入电机的额定功率(kW)、电机的额定转速(r/min)、转子外径(m)、铁心长(m)转子槽数、电机极对数 等参数&#xff0c;输出转速变化、力矩变化等结果。程序已调通&#xff0c;可直接运行。 246 交流电机动态 转速…

DAY 2

winget.cpp #include "widget.h"Widget::Widget(QWidget *parent): QWidget(parent) {this->resize(540,415);this->setFixedSize(540,415);//窗口标题this->setWindowTitle("盗版QQ");//窗口图标this->setWindowIcon(QIcon("E:\\qq\\pi…

30分钟打造属于自己的Flutter内存泄漏检测工具---FlutterLeakCanary

30分钟打造属于自己的Flutter内存泄漏检测工具 思路检测Dart 也有弱引用-----WeakReference如何执行Full GC&#xff1f;如何知道一个引用他的文件路径以及类名&#xff1f; 代码实践第一步&#xff0c;实现Full GC第二步&#xff0c;如何根据对象引用&#xff0c;获取出他的类…

HarmonyOS实战开发教程-如何开发一个2048游戏

今天为大家分享的是2048小游戏&#xff0c;先看效果图&#xff1a; 这个项目对于新手友友来说可能有一点难度&#xff0c;但是只要坚持看完一定会有收获。因为小编想分享的并不局限于ArkTs语言&#xff0c;而是编程思想。 这个游戏的基本逻辑是初始化一个4乘4的数组&#xff…

直接显示二进制图片

Option Explicit Private Type GUID Data1 As Long Data2 As Integer Data3 As Integer Data4(7) As Byte End Type Private Declare Function CreateStreamOnHGlobal Lib “ole32.dll” (ByRef hGlobal As Any, ByVal fDeleteOnResume As Long, ByRef ppstr As Any) As Long P…

LeetCode 每日一题 Day 144-157

2385. 感染二叉树需要的总时间 给你一棵二叉树的根节点 root &#xff0c;二叉树中节点的值 互不相同 。另给你一个整数 start 。在第 0 分钟&#xff0c;感染 将会从值为 start 的节点开始爆发。 每分钟&#xff0c;如果节点满足以下全部条件&#xff0c;就会被感染&#xf…

网络基础(1)网络编程套接字TCP,守护进程化

TCP协议 下面我们来学习一下TCP套接字的使用。 也就是使用一下基本的接口。首先TCP套接字的使用和UDP套接字的使用是大同小异的&#xff0c;但是多了一些步骤。 这里回顾一下&#xff1a;UDP是不可靠的&#xff0c;无连接的协议。而TCP则是可靠的&#xff0c;面向连接的协议…

【Linux】CAN相关命令:ip、ifconfig、can-utils

1、配置CAN波特率 ip link set can0 type can bitrate 500002、启动CAN设备 ip link set can0 up 或者 ifconfig can0 up3、显示CAN设备信息 ip -d -s link show can0ip -d -s link show can0 2: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc fq_codel state UNKNOWN…

[HUBUCTF 2022 新生赛]checkin

数组反序列化弱比较 <?php $info array(username>true,password>true); echo serialize($info); ?> //?infoa:2:{s:8:"username";b:1;s:8:"password";b:1;}1.构造不能用类&#xff0c;因为$data_unserialize只是一个变量&#xff0c;不能…

谷歌确认:链接并不那么重要

谷歌的 Gary Illyes 在最近的一次搜索营销会议上证实&#xff0c;谷歌只需要很少的链接&#xff0c;这为出版商需要关注其他因素提供了越来越多的证据。Gary 在推特上证实了他确实说过这些话。 排名链接的背景 20 世纪 90 年代末&#xff0c;搜索引擎发现链接是验证网站权威性…