Android 系统源码项目加载预编好的so库

news/2024/9/17 7:08:55/ 标签: android, gitee

Android 系统源码项目加载预编好的so库

文章目录

  • Android 系统源码项目加载预编好的so库
    • 一、前言
    • 二、源码中加载so
      • 1、Android.mk加载so
        • 加载so的主要相关代码:
      • 2、Android.bp加载so
        • (1)Android.mk使用源码命令编译成Android.bp
        • (2)Android.bp
        • Android.bp 不用编译so并进行读取so解决方法:
    • 三、其他
      • 1、系统源码加载编译so小结
      • 2、Android.mk中可以执行 PRODUCT_COPY_FILES 吗?
      • 3、mk 添加编译androidx相关包报错解决
      • 4、Android.mk 在源码中执行执行转换成Android.bp

一、前言

Android 系统开发中会源代码有加载so库的情况,本文进行简单介绍。

Android Studio 项目加载so库,已经有介绍过:

https://blog.csdn.net/wenzhi20102321/article/details/137080360

Android Studio加载so和使用so还是比较方便的,环境OK的情况,几分钟就可以完成验证测试了。

但是如果要在系统源代码Java里面加载和使用so就比较麻烦了,

主要通过Android.mk或者Android.bp编译加载so,反正网上的代码写的mk/bp基本都是不行的。

本来以为只是介绍一下编译使用的Android.mk或者Android.bp就可以了,

但是事实有点出乎意料,总结下来发现收获了一些知识点;

所以这个很有必要进行总结一下。有兴趣的可以看看。

普通应用开发不一定用到这个知识,但是系统编译开发会用到这个知识,并且有的相关文件还不好解决。

本文最后还有一些源码编译apk相关知识的总结,还是比较有用的。可以点赞收藏。

二、源码中加载so

源码中加载so,首选是Android.mk,因为Android.bp 未完善,会有bug。

1、准备源码

这里准备的源码和Android Studio 加载so的源码是一模一样的,
只是在app同级目录下加一个Android.mk 或者Android.bp。

1、Android.mk加载so

LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)
LOCAL_PACKAGE_NAME := JniLoadsoLOCAL_MODULE_TAGS := optional
LOCAL_CERTIFICATE := platform
LOCAL_PRIVATE_PLATFORM_APIS := true
#生成到system/priv-app目录
LOCAL_PRIVILEGED_MODULE := trueLOCAL_DEX_PREOPT := falseLOCAL_MANIFEST_FILE := \app/src/main/AndroidManifest.xmlLOCAL_SRC_FILES := \$(call all-java-files-under,app/src/main/java/) \LOCAL_RESOURCE_DIR := \$(LOCAL_PATH)/app/src/main/resLOCAL_PREBUILT_JNI_LIBS:= \app/jni/arm64-v8a/libnative-lib.so
#这样加没用!!不知为啥
#LOCAL_SHARED_LIBRARIES := libnative-libLOCAL_STATIC_ANDROID_LIBRARIES := \androidx.recyclerview_recyclerview \com.google.android.material_materialLOCAL_STATIC_JAVA_LIBRARIES := \androidx.appcompat_appcompat \androidx-constraintlayout_constraintlayoutinclude $(BUILD_PACKAGE)include $(CLEAR_VARS)
LOCAL_MODULE := libnative-lib
LOCAL_SRC_FILES := app/jni/arm64-v8a/libnative-lib.so
include $(PREBUILT_SHARED_LIBRARY)

上面加了Androidx的包,需要在AndroidManifest.xml进行适配修改一下否则会编译报错。具体如何适配最后有说明;Android13或者Android14 源码编译会有这个问题。

这里的Android.mk 直接放在app同级目录下面就行,

我不喜欢放两个Android.mk的处理,实际上很多源码应用是这样处理的:
app同级目录下放一个声明链接的Android.mk,app/src/main/ 下面也放一个实际起作用的Android.mk;

加载so的主要相关代码:
LOCAL_PREBUILT_JNI_LIBS:= \app/jni/arm64-v8a/libnative-lib.so
#这样加没用!!不知为啥
#LOCAL_SHARED_LIBRARIES := libnative-libinclude $(CLEAR_VARS)
LOCAL_MODULE := libnative-lib
LOCAL_SRC_FILES := app/jni/arm64-v8a/libnative-lib.so
include $(PREBUILT_SHARED_LIBRARY)

上面和so相关就就LOCAL_PREBUILT_JNI_LIBS这一行代码,其他的编译命令和编译普通Java代码项目是一样的。

加入这个后,系统会在apk的同级目录下生成so文件。

后续有尝试去除 PREBUILT_SHARED_LIBRARY ,其实也是可以生成so在lib目录的。

所以主要作用的文件就是:

LOCAL_PREBUILT_JNI_LIBS:= \app/jni/arm64-v8a/libnative-lib.so

out目录生成后的so文件,如下图所示:

在这里插入图片描述

整编整个大包,烧录到系统,这个so就会在运行的Android设备apk的lib目录下。具体情况:

rk3588_t:/system/priv-app/JniLoadso #
rk3588_t:/system/priv-app/JniLoadso # ls
JniLoadso.apk  lib  oat
rk3588_t:/system/priv-app/JniLoadso # ls lib/arm64/
libnative-lib.so
rk3588_t:/system/priv-app/JniLoadso #

app应用运行后是可以正常读取到so库的。

2、Android.bp加载so

Android.bp 如何编写?

其实系统源码中有个android.go 文件是写了Android.mk和Android.bp所有对于属性的转换关系的。

但是并不是所有的标签都能转换。估计是实现了比较常用的大部分功能。

(1)Android.mk使用源码命令编译成Android.bp

源码中如何实现转换的,最后有链接,有兴趣的可以看看。

通过转换后获取到Android.bp:

android_app {name: "JniLoadso",certificate: "platform",platform_apis: true,//生成到system/priv-app目录privileged: true,dex_preopt: {enabled: false,},manifest: "app/src/main/AndroidManifest.xml",srcs: ["app/src/main/java//**/*.java"],resource_dirs: ["app/src/main/res"],// ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_PREBUILT_JNI_LIBS// LOCAL_PREBUILT_JNI_LIBS := app/jni/arm64-v8a/libnative-lib.so//这样加没用!!不知为啥//LOCAL_SHARED_LIBRARIES := libnative-libstatic_libs: ["androidx.recyclerview_recyclerview","com.google.android.material_material","androidx.appcompat_appcompat","androidx-constraintlayout_constraintlayout",],}// ANDROIDMK TRANSLATION ERROR: unsupported include
// include $(PREBUILT_SHARED_LIBRARY)

虽然生成了Android.bp但是最后提示有ERROR,并且LOCAL_PREBUILT_JNI_LIBS指令是无法转换的。

所以Android使用源码命令转换Android.mk,在一些特殊的模块处理上是有问题的,比如so加载就有问题。

并且在mk和bp转换的关系源码android.go中也是未找到 LOCAL_PREBUILT_JNI_LIBS 的转换关系;

所以这个是目前Android14 也无法解决的问题;使用Android.bp就是无法编译加载so。

但是上面的Android.bp 是可以正常编译出apk的,只是编译出的out目录apk中未生成lib目录。

所以Android.bp 没法编译加载so项目吗,其实不是。

因为在源码中这样编译so项目是没问题的。但是要多做一些操作。往后看看吧。

(2)Android.bp

有用部分的源码:

android_app {name: "JniLoadso",platform_apis: true,certificate: "platform",optimize: {proguard_flags_files: ["app/src/main/proguard.flags"],},privileged: true,static_libs: ["androidx.appcompat_appcompat","androidx-constraintlayout_constraintlayout","androidx.annotation_annotation","com.google.android.material_material","androidx-constraintlayout_constraintlayout","androidx.recyclerview_recyclerview",],manifest: "app/src/main/AndroidManifest.xml",srcs: ["app/src/main/java/**/*.java",],resource_dirs: ["app/src/main/res"],}

这样编译后运行的设备中未包含 JniLoadso 应用需要的so,那么点击运行应用肯定会报错的,怎么解决?

Android.bp 不用编译so并进行读取so解决方法:
1、Android 系统编译源码应用可以读取系统so;
如果apk应用是64位的(目前99%应用都是64位的),那么系统可以读取到system/lib64下面的so;
2、如果是限制单个应用使用,可以通过脚本把so放到运行环境的apk的lib/arm64/目录下;
3、apk优先是读取自己目录下的lib,如果没有就读取系统下的 system/lib64的so
这是亲自测试验证过的,两个地方只有一个地方有就可以正常读取到。

其实如何读取不用我们管,系统会实现;

具体要管的就是系统运行环境有没有放入so文件,有就可以读取到,没有就会报错。

系统编译的app应用代码中只要定义了加载so

    static {System.loadLibrary("native-lib");}

那如果运行环境中如果存在so就没有问题。

原因和思路找到了,源码里面怎么写这个脚本呢?

1、复制文件到system/lib64PRODUCT_COPY_FILES += device/copyfile/libnative-lib.so:$(TARGET_COPY_OUT_SYSTEM)/lib64/libnative-lib.so//PRODUCT_COPY_FILES 目录是以release目录为基准的;
//copyfile 这个文件夹是自己添加的,自定义的,可以根据自己情况设置。2、复制文件到某个apk的目录下:
PRODUCT_COPY_FILES += device/copyfile/libnative-lib.so:$(TARGET_COPY_OUT_SYSTEM)/priv-app/JniLoadso/lib/arm64/libnative-lib.so

PRODUCT_COPY_FILES 指令,相信很多搞过系统源码都是知道的,这个就是复制文件的;

在device下的某个mk文件都有这个指令,在device目录其中一个起作用的写入上面的指令就可以。

正常情况下,写了上面指令,系统源码编译后就会在out目录下生成so文件,

比如写了第一条指令,就会在out/target/XXX…/system/lib64 看到 libnative-lib.so 文件。

三、其他

1、系统源码加载编译so小结

(1) Android.mk 中可以使用 LOCAL_PREBUILT_JNI_LIBS指令复制so到编译的XXXapk/lib/arm64/目录下;
(2) Android.bp 没有指令复制so到编译的apk目录下;
(3) Android 系统编译源码应用可以读取系统so和apk目录下的lib/arm64/的so文件
(4) 如果是使用Android.bp编译so项目,
必须要写脚本命令复制so到system/lib64 或者复制到apk的ib/arm64/
(5) 如果是使用Android.mk编译so项目,
即可使用LOCAL_PREBUILT_JNI_LIBS指令复制so,也可以在另外的mk文件使用脚本复制so文件到system/lib64 或者复制到apk的ib/arm64/
(6) 如果是单个应用使用的so,使用复制脚本命令把so文件复制到到apk的ib/arm64/就可以;如果是多个应用使用到的so,使用复制脚本命令把so文件复制到到system/lib64/比较好。

其实那些编译apk的指令都是差不多的,很多网上搜索到不可用的指令,参考系统源码其他apk编译的就可以;

最重要的是学习到了系统中编译源码apk 会读取 apk的ib/arm64/或者system/lib64 下面的so;

加载so一定是以so文件名称作为唯一标识的,如果so文件名称不对是读取不到的;

Java代码中读取so:

    static {System.loadLibrary("native-lib");}

那么so的名称一定要设置成 libnative-lib.so(native-lib.so是不行的),也就是说前面加lib,后面加.so,就是so文件的命名。

2、Android.mk中可以执行 PRODUCT_COPY_FILES 吗?

很多人可能没试过,不确定有没有作用。

我之前也么试过,今天特意试了一下,确实不行。

Android.mk中可以执行 PRODUCT_COPY_FILES 会报错:

FAILED: 
In file included from build/make/core/prebuilt.mk:53:
packages/SkgApps/JniLoadso/Android.mk:36: error: cannot assign to readonly variable: PRODUCT_COPY_FILES

其实想一下就知道了,Android.mk里面应该是执行有限的指令和标签的;

android.go里面确实是没有这个 PRODUCT_COPY_FILES 指令的。

androidx_373">3、mk 添加编译androidx相关包报错解决

AndroidManifest.xml.fixed 的分析处理:

https://blog.csdn.net/wenzhi20102321/article/details/138334484

4、Android.mk 在源码中执行执行转换成Android.bp

Android.mk和Android.bp的区别和转换详解:

https://blog.csdn.net/wenzhi20102321/article/details/135704801


http://www.ppmy.cn/news/1522254.html

相关文章

SpringBoot教程(十五) | SpringBoot集成RabbitMq(死信队列、延迟队列)

SpringBoot教程(十五) | SpringBoot集成RabbitMq(死信队列、延迟队列) (一)死信队列使用场景具体用法前提示例: (二)延迟队列使用场景方法一:通过死亡队列实现方法二&…

【Oracle点滴积累】解决IMP-00017、ORA-20005、ORA-06512错误的方法

广告位招租! 知识无价,人有情,无偿分享知识,希望本条信息对你有用! 今天和大家分享 IMP-00017: folloging statement failed with ORACLE error 20005 ORA-20005: object statistics are locked (stattype ALL) 错…

WordPress上可以内容替换的插件

插件下载地址:WordPress内容替换插件 – 果果开发 类型 替换的类型:文章、自定义文章类型、分类、标签、媒体库、页面、评论、数据库表,不同的类型可以替换不同的字段。 替换字段 替换的字段,哪些字段内容需要替换。除了数据库…

Q215 数组中第K大的元素

思路 可以用排序,但是不用全有序 还有个要求是O(n) 快排改版 快排只排需要的部分 public int findKthLargest(int[] nums, int k) {return quickSort(nums, 0, nums.length-1, nums.length-k);}public static int quickSort(int[] nums, …

JVM3-双亲委派机制

目录 概述 作用 如何指定加载类的类加载器? 面试题 打破双亲委派机制 自定义类加载器 线程上下文类加载器 Osgi框架的类加载器 概述 由于Java虚拟机中有多个类加载器,双亲委派机制的核心是解决一个类到底由谁加载的问题 双亲委派机制&#xff…

2409wtl,切换视图

原文 介绍 我从一个基于SDI(单文档接口)WTL向导的应用开始,添加了一些从控件继承的窗口和一些对话框窗口(表单视图),然后才发现我必须,使SDI框架动态加载和卸载子窗口. 本文演示了两个可用来完成的技术:在SDI应用中的视图间动态切换.这是我使用的两个. 技术 1技术:第一个方…

指针作为函数参数详解

一级指针传参 形参指针的指向没有被改变 void test(int* p1) {*p1 8; }int main() {int a 5;int* p &a;test(p);printf("%d\n", a); }输出 8总结: 由代码和上图可知,实参p是个指针,其值为变量a的地址,将其传参给形参p1&…

webpack+lite-server 构建项目示例

首先安装以下库 npm install --save-dev webpack webpack-cli lite-server npm install --save-dev babel-loader babel/core babel/preset-env项目结构 webpack.config.js 配置 const path require("path");module.exports {entry: "./src/index.js",…

5G前传-介绍

1. 引用 知识分享系列一:5G基础知识-CSDN博客 5G前传的最新进展-CSDN博客 灰光和彩光_通信行业5G招标系列点评之二:一文读懂5G前传-光纤、灰光、彩光、CWDM、LWDM、MWDM...-CSDN博客 术语: 英文缩写描述‌BBU:Building Baseba…

华为云征文|Flexus云服务X实例安装ODBC驱动,在ODBC中建立MySQL数据库连接,通过QT连接云数据库

引出 4核12G-100G-3M规格的Flexus X实例使用测评第2弹:Flexus云服务X实例安装ODBC驱动,在ODBC中建立MySQL数据库连接,通过QT连接云数据库 什么是Flexus云服务器X实例 官方解释: Flexus云服务器X实例是新一代面向中小企业和开发…

基于发布-订阅模型的音视频流分发框架

有时需要同时网络推流和把流封装为某格式,或做一些其它操作。这就需要一个分发流的机制,把同一路流分发给多个使用者去操作,下面实现了一个简易的线程安全的音视频流分发框架。代码如下: avStreamHub.h #ifndef STREAMHUB_H #def…

算法专题一: 双指针

目录 前言1. 移动零(easy)2. 复写零(easy)3. 快乐数(medium)4. 盛水最多的容器(medium)5. 有效三角形的个数(medium)6. 和为 s 的两个数字(easy&a…

Docker 进阶构建:镜像、网络与仓库管理

目录 三. docker镜像构建 1. docker镜像结构 2. 镜像运行的基本原理 3. 镜像获得方式 4. 镜像构建 5. Dockerfile实例 6. 镜像优化方案 6.1. 镜像优化策略 6.2. 镜像优化示例:缩减镜像层 6.3. 镜像优化示例:多阶段构建 6.4. 镜像优化示例:使用最精简镜像 四. docke…

网络安全服务基础Windows--第15节-CA与HTTPS理论

公钥基础设施(Public Key Infrastructure,简称 PKI)是指⼀套由硬件、软件、⼈员、策略和程序组成的系统,⽤于创建、管理、分发、使⽤、存储和撤销数字证书。PKI 的核⼼⽬的是通过使⽤公钥加密技术来确保电⼦通信的安全性。PKI 为数…

八月二十九日(day 39)docker6

1.前端(nginx) [rootlocalhost ~]# docker pull nginx //拉取nginx镜像 [rootlocalhost ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE nginx latest 5ef79149e0ec 2 we…

springboot数据库连接由localhost改成IP以后访问报错500(2024/9/7

步骤很详细,直接上教程 情景复现 一.没改为IP之前正常 二.改完之后报错 问题分析 SQL没开启远程连接权限 解决方法 命令行登入数据库 mysql -u root -p切换到对应数据库 use mysql;设置root用户的连接权限允许其他IP连接数据库 update user set host % whe…

前端技术(六)—— AJAX详解

一、原生 AJAX 1. AJAX 简介 AJAX 全称为 Asynchronous JavaScript And XML,就是异步的 JS 和 XML。 通过 AJAX 可以在浏览器中向服务器发送异步请求,最大的优势:无刷新获取数据。 AJAX 不是新的编程语言,而是一种将现有的标准组…

C语言程序设计(初识C语言后部分)

留一片空白,随时浓墨重彩。 二十,结构体 结构体类型的声明 结构体初始化 结构体成员访问 结构体传参 1.结构体的声明 1)结构的基础知识 结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。 2&…

上海网站设计-网站手机端制作

随着移动互联网的迅猛发展,越来越多的人通过手机上网,这使得网站手机端的设计和制作变得尤为重要。在这种背景下,上海的网站设计行业迎来了新的机遇与挑战。 首先,网站手机端制作的必要性不容忽视。根据统计数据显示,手…

Flask框架 完整实战案例 附代码解读 【3】

Flask 是一个轻量级的可定制框架,使用Python语言编写,较其他同类型框架更为灵活、轻便、安全且容易上手。 前面已经写过项目从新建运行安装到测试部署的全流程,其中有写Flask框架从新建到部署全流程,但是只有部分代码。本篇主要是…