Android 生成SO - 基础工程创建

news2024/11/22 14:23:42

最近需要给小伙伴扫盲一下如何使用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 <string>
#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());
}

extern "C" JNIEXPORT jstring JNICALL

在 JNI(Java Native Interface)中,extern "C" 用于指定 C++ 函数按照 C 语言的命名和调用约定来处理。JNIEXPORTJNICALL 是 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 应用默认会将 stdoutstderr 重定向到 /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());
    }

    /**
     * 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;
}

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

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

相关文章

coreldraw是什么软件 矢量图设计软件推荐 CDR学习入门 CDR2024发布 cdr2024免费激活下载

CDR是一款平面设计软件&#xff0c;全称为CorelDRAW&#xff0c;是由Corel公司开发的矢量图形编辑工具套件。这款软件结合了矢量图形设计和页面布局的全功能&#xff0c;被广泛应用于商标设计、标志制作、模型绘制、插图描画、排版及分色输出等诸多领域。 2024年3月&#xff0…

STM32进阶笔记——FATFS文件系统(下)

本专栏争取每周三更新直到更新完成&#xff0c;期待大家的订阅关注&#xff0c;欢迎互相学习交流。 本文需要一些SD卡和内存管理等前置知识&#xff0c;后续文章会介绍&#xff0c;这里先介绍一下FATFS文件系统。关于FATFS的文章分为上下两篇&#xff0c;上篇主要介绍什么是FAT…

【C语言】tcp_transmit_skb

一、__tcp_transmit_skb讲解 这个函数 __tcp_transmit_skb() 是 Linux 内核中 TCP/IP 协议栈的一部分&#xff0c;负责处理传输控制协议&#xff08;TCP&#xff09;数据包的发送。具体来说&#xff0c;这个函数将 TCP 头部添加到一个没有任何头部信息的 socket buffer (sk_bu…

【大模型API调用初尝试二】星火认知大模型 百度千帆大模型

大模型API调用初尝试二 科大讯飞—星火认知大模型单论会话调用多轮会话调用 百度—千帆大模型获取access_token单轮会话多轮会话 科大讯飞—星火认知大模型 星火认知大模型是科大讯飞开发的&#xff0c;直接使用可以点击星火认知大模型&#xff0c;要调用API的话在讯飞开发平台…

python文本转语音的开源库介绍

本文介绍Python中用于文本转语音&#xff08;Text-to-Speech, TTS&#xff09;的三个开源库&#xff1a;pyttsx3、gtts和espeak。这些库可以在Windows平台上运行&#xff0c;并允许用户将文本转换为语音输出。 pyttsx3 pyttsx3是一个Python库&#xff0c;用于跨平台的文本到语音…

PLC远程故障诊断

随着工业自动化水平的不断提升&#xff0c;可编程逻辑控制器&#xff08;PLC&#xff09;在生产线控制、设备自动化等领域发挥着越来越重要的作用。然而&#xff0c;PLC在运行过程中难免会出现故障&#xff0c;影响生产线的稳定运行。因此&#xff0c;如何迅速、准确地诊断并解…

jenkins部署go应用 基于docker

丢弃旧的的构建 github 拉取代码 拉取代码排除指定配置文件 报错 环境变量失效 服务器版本为1.21.6 但是一直没有生效

HTML 01

1.html使用标签来表达 结束标签多一个/ <strong>文字内容</strong> <hr> 包裹内容就是双标签&#xff0c;换行等是单标签 浏览器中显示内容&#xff1a; 2.html的骨架是网页模板 <!DOCTYPE html> <html lang"en"> <head>&l…

惬意了解 —— 前端发展史

下拉底部&#xff0c;参与投票&#xff5e;&#xff5e; 前端发展史&#xff1a;从洪荒时代到现代 前端开发已经走过了将近20年的历程&#xff0c;从最早的纯静态页面到如今的现代前端框架&#xff0c;我们见证了前端技术的蓬勃发展。让我们一起回顾这段历史。 洪荒时代&…

WordPress供求插件API文档:获取市场类型

请注意&#xff0c;该文档为&#xff1a; WordPress供求插件&#xff1a;一款专注于同城生活信息发布的插件-CSDN博客文章浏览阅读396次&#xff0c;点赞6次&#xff0c;收藏5次。WordPress供求插件&#xff1a;sliver-urban-life 是一款专注于提供同城生活信息发布与查看的插件…

TypeScript 哲学 - index access types

We can use an indexed access type to look up a specific property on another type: 快速得到一个数组类型 和 得到一个对象数组中单个元素的类型 用 infer推出数组类型 &#xff08;typescript structure type &#xff1a; as long as my more than yours &#xff0…

耐腐蚀PFA气体洗涤瓶可多级串联透明特氟龙塑料氢气吸收装置

洗气瓶是一种常用于净化和干燥各种气体的实验室器皿&#xff0c;以去除其中的水分、油脂、颗粒物等杂质&#xff0c;从而使需要用到的气体满足实验要求。 PFA洗气瓶的工作原理&#xff1a; 主要是通过液体吸收、溶解或发生化学反应来去除气体中的杂质。在洗气过程中&#xff…

LeetCode108题:将有序数组转换为二叉搜索树(python3)

一个容易想到的思路&#xff1a;使用 nums 中最靠近中心的位置作为整棵 BST 的根节点&#xff0c;确保左右子树节点数量平衡。随后递归构造 nums 中下标范围为 [0,mid−1]作为左子树&#xff0c;递归构造 nums 中下标范围为 [mid1,n−1]作为右子树。 # Definition for a binar…

10、设计模式之外观模式(Facade)

一、什么是外观模式 这个大家一定是经常使用的&#xff0c;外观模式&#xff08;门面模式&#xff09;是一种结构型设计模式。它提供一个统一的接口&#xff0c;用于访问子系统中的一组接口&#xff0c;隐藏了系统的复杂性。最简单的应用就是&#xff0c;当controller层的逻辑处…

19、设计模式之中介者模式(Mediator)

一、什么是中介者模式 中介者模式是一种行为型设计模式&#xff0c;它用于减少对象之间互相通信的复杂性。中介者模式通过创建一个中介者对象&#xff0c;将对象之间的通信集中交给该对象来处理&#xff0c;而不是直接相互交流&#xff0c;是符合迪米特原则的典型应用。 迪米特…

creator-webview加载优化

title: creator-webview加载优化 categories: Cocos2dx tags: [cocos2dx, creator, webview, 优化, 加载, 性能] date: 2024-03-02 13:17:20 comments: false mathjax: true toc: true creator-webview加载优化 前篇 Android WebView shouldInterceptRequest - https://www.ji…

得物布局构建耗时优化方案实践

一、背景 当谈到移动应用程序的体验时&#xff0c;页面启动速度是其中至关重要的一点&#xff0c;更快的页面展示速度确保应用程序可以迅速加载并响应用户的操作, 从而提高用户使用 App 时的满意度。在页面启动的整个流程中&#xff0c;随着 UI 复杂度的上升&#xff0c;布局的…

jmeter发送请求参数如何使用变量

问题描述 发送jmeter请求时&#xff0c;想设置请求参数为变量 解决方法

190基于matlab的tfrSTFT时频分布图

基于matlab的tfrSTFT时频分布图&#xff0c;计算时间序列的STFT时频分布图&#xff0c;得到瞬时频率。通过GUI可以调节图像的展示样式。程序已调通&#xff0c;可直接运行。 190 STFT时频分布图 瞬时频率 能量谱 (xiaohongshu.com)

剪切板工具Ditto

https://github.com/sabrogden/Ditto/releases 1.开源免费Ditto 快捷键 ctrl (j键盘 esc下面的符号) 挺好用的&#xff0c;解决问题 投标中需要重复填写的内容&#xff0c;可以通过他进行 实时复制信息 2.windows自带的黏贴板工具 win键v调出快捷键