Android插件化(三)基础之Android应用程序资源的编译和打包过程分析

Android插件化(三)基础之Android应用程序资源的编译和打包过程分析

Android资源加载常规思路

getResourcesForApplication
//首先,通过包名获取该包名的Resources对象
Resources res= pm.getResourcesForApplication(packageName);
//根据约定好的名字,去取资源id;
int id=res.getIdentifier("a","drawable",packageName);//根据名字取id
//根据资源id,取出资源
Drawable drawable=res.getDrawable(id)

Android Apk打包流程

  1. 打包资源文件,生成R.java文件;
  2. 处理aidl文件,生成相应java文件;
  3. 编译工程源文件,生成相应class文件;
  4. 转换所有class文件,生成classes.dex文件;
  5. 打包生成apk文件;
  6. 对apk文件进行签名;
  7. 对签名后的apk文件进行对齐处理;

在这里插入图片描述

打包过程使用的工具

名称功能介绍在操作系统中的路径源码路径
aapt(Android Asset Package Tool)Android资源打包工具${ANDROID_SDK_HOME} /build-tools/ ANDROID_VERSION/aaptframeworks\base\tools\aap
aidl(android interface definition language)Android接口描述语言,将aidl转化为.java文件的工具${ANDROID_SDK_HOME}/build-tools/ ANDROID_VERSION/aidlframeworks\base\tools\aidl
javacJava Compiler${JDK_HOME}/javac/usr/bin/javac
dex转化.class文件为Davik VM能识别的.dex文件${ANDROID_SDK_HOME}/build-tools/ ANDROID_VERSION/dx
apkbuilder生成apk包${ANDROID_SDK_HOME}/tools/apkbuildersdk\sdkmanager\libs\sdklib\ src\com\android\sdklib\build\ApkBuilderMain.java
jarsigner.jar文件的签名工具${JDK_HOME}/jarsigner或/usr/bin/jarsigner
zipalign字节码对齐工具${ANDROID_SDK_HOME}/tools/zipalign
第一步: 打包资源文件,生成R.java文件

【输入】Resource文件(就是工程中res中的文件)、Assets文件(相当于另外一种资源,这种资源Android系统并不像对res中的文件那样优化它)、AndroidManifest.xml文件(包名就是从这里读取的,因为生成R.java文件需要包名)、Android基础类库(Android.jar文件)
【工具】aapt工具
【输出】打包好的资源(bin目录中的resources.ap_文件)、R.java文件(gen目录中)
打包资源的工具aapt,大部分文本格式的XML资源文件会被编译成二进制格式的XML资源文件,除了assets和res/raw资源被原装不动地打包进APK之外,其它的资源都会被编译或者处理。 。
生成过程主要是调用了aapt源码目录下的Resource.cpp文件中的buildResource()函数,该函数首先检查AndroidManifest.xml的合法性,然后对res目录下的资源子目录进行处理,处理的函数为makeFileResource(),处理的内容包括资源文件名的合法性检查,向资源表table添加条目等,处理完后调用compileResourceFile()函数编译res与asserts目录下的资源并生成resources.arsc文件,compileResourceFile()函数位于aapt源码目录的ResourceTable.cpp文件中,该函数最后会调用parseAndAddEntry()函数生成R.java文件,完成资源编译后,接下来调用compileXmlfile()函数对res目录的子目录下的xml文件分别进行编译,这样处理过的xml文件就简单的被“加密”了,最后将所有的资源与编译生成的resorces.arsc文件以及“加密”过的AndroidManifest.xml文件打包压缩成resources.ap_文件(使用Ant工具命令行编译则会生成与build.xml中“project name”指定的属性同名的ap_文件)。
关于这一步更详细的流程可阅读http://blog.csdn.net/luoshengyang/article/details/8744683

res目录有9种目录

  • –animator。这类资源以XML文件保存在res/animator目录下,用来描述属性动画。
  • –anim。这类资源以XML文件保存在res/anim目录下,用来描述补间动画。
  • –color。这类资源以XML文件保存在res/color目录下,用描述对象颜色状态选择子。
  • –drawable。这类资源以XML或者Bitmap文件保存在res/drawable目录下,用来描述可绘制对象。例如,我们可以在里面放置一些图片(.png, .9.png, .jpg, .gif),来作为程序界面视图的背景图。注意,保存在这个目录中的Bitmap文件在打包的过程中,可能会被优化的。例如,一个不需要多于256色的真彩色PNG文件可能会被转换成一个只有8位调色板的PNG面板,这样就可以无损地压缩图片,以减少图片所占用的内存资源。
  • –layout。这类资源以XML文件保存在res/layout目录下,用来描述应用程序界面布局。
  • –menu。这类资源以XML文件保存在res/menu目录下,用来描述应用程序菜单。
  • –raw。这类资源以任意格式的文件保存在res/raw目录下,它们和assets类资源一样,都是原装不动地打包在apk文件中的,不过它们会被赋予资源ID,这样我们就可以在程序中通过ID来访问它们。例如,假设在res/raw目录下有一个名称为filename的文件,并且它在编译的过程,被赋予的资源ID为R.raw.filename,那么就可以使用以下代码来访问它:Resources res = getResources(); InputStream is = res .openRawResource(R.raw.filename);
  • –values。这类资源以XML文件保存在res/values目录下,用来描述一些简单值,例如,数组、颜色、尺寸、字符串和样式值等,一般来说,这六种不同的值分别保存在名称为arrays.xml、colors.xml、dimens.xml、strings.xml和styles.xml文件中。
  • –xml。这类资源以XML文件保存在res/xml目录下,一般就是用来描述应用程序的配置信息。
第二步:处理aidl文件,生成相应的java文件。

输入】源码文件、aidl文件、framework.aidl文件
【工具】aidl工具
【输出】对应的.java文件
对于没有使用到aidl的android工程,这一步可以跳过。aidl工具解析接口定义文件并生成相应的java代码供程序调用。

第三步:编译工程源代码,生成下相应的class文件。

【输入】源码文件(包括R.java和AIDL生成的.java文件)、库文件(.jar文件)
【工具】javac工具
【输出】.class文件
这一步调用了javac编译工程src目录下所有的java源文件,生成的class文件位于工程的bin\classes目录下,上图假定编译工程源代码时程序是基于android SDK开发的,实际开发过程中,也有可能会使用android NDK来编译native代码,因此,如果可能的话,这一步还需要使用android NDK编译C/C++代码,当然,编译C/C++代码的步骤也可以提前到第一步或第二步。

第四步:转换所有的class文件,生成classes.dex文件。

【输入】 .class文件(包括Aidl生成.class文件,R生成的.class文件,源文件生成的.class文件),库文件(.jar文件)
【工具】javac工具
【输出】.dex文件
前面多次提到,android系统dalvik虚拟机的可执行文件为dex格式,程序运行所需的classes.dex文件就是在这一步生成的,使用的工具为dx,dx工具主要的工作是将java字节码转换为dalvik字节码、压缩常量池、消除冗余信息等。

第五步:打包生成apk。

【输入】打包后的资源文件、打包后类文件(.dex文件)、libs文件(包括.so文件,当然很多工程都没有这样的文件,如果你不使用C/C++开发的话)
【工具】apkbuilder工具
【输出】未签名的.apk文件
打包工具为apkbuilder,apkbuilder为一个脚本文件,实际调用的是android-sdk\tools\lib\sdklib.jar文件中的com.android.sdklib.build.ApkBuilderMain类。它的代码实现位于android系统源码的sdk\sdkmanager\libs\sdklib\src\com\android\sdklib\build\ApkBuilderMain.java文件,代码构建了一个ApkBuilder类,然后以包含resources.arsc的文件为基础生成apk文件,这个文件一般为ap_结尾,接着调用addSourceFolder()函数添加工程资源,addSourceFolder()会调用processFileForResource()函数往apk文件中添加资源,处理的内容包括res目录与asserts目录中的文件,添加完资源后调用addResourceFromJar()函数往apk文件中写入依赖库,接着调用addNativeLibraries()函数添加工程libs目录下的Native库(通过android NDK编译生成的so或bin文件),最后调用sealApk()关闭apk文件。

第六步:对apk文件进行签名。

【输入】未签名的.apk文件
【工具】jarsigner
【输出】签名的.apk文件
android的应用程序需要签名才能在android设备上安装,签名apk文件有两种情况:一种是在调试程序时进行签名,使用eclipse开发android程序时,在编译调试程序时会自己使用一个debug.keystore对apk进行签名;另一种是打包发布时对程序进行签名,这种情况下需要提供一个符合android开发文档中要求的签名文件。签名的方法也分两种:一种是使用jdk中提供的jarsigner工具签名;另一种是使用android源码中提供的signapk工具,它的代码位于android系统源码build\tools\signapk目录下。

第七步:对签名后的apk文件进行对齐处理。

【输入】签名后的.apk文件
【工具】zipalign工具
【输出】对齐后的.apk文件
这一步需要使用的工具为zipalign,它位于android-sdk\tools目录,源码位于android系统源码的build\tools\zipalign目录,它的主要工作是将spk包进行对齐处理,使spk包中的所有资源文件距离文件起始偏移为4字节整数倍,这样通过内存映射访问apk文件时速度会更快,验证apk文件是否对齐过的工作由ZipAlign.cpp文件的verify()函数完成,处理对齐的工作则由process()函数完成。

以一个具体项目中包含的具体文件为例作图如下:
在这里插入图片描述

APK文件内容解析

android的项目经过编译和打包,形成了:

  • .dex 文件
  • resources.arsc
  • uncompiled resources
  • AndroidManifest.xml

解压一个普通的apk文件,解压出来的文件如下:

  • META-INF文件夹
  • res文件夹
  • AndroidManifest.xml
  • classes.dex
  • resources.arsc

classes.dex 是.dex文件。
resources.arsc是resources resources文件。
AndroidManifest.xml是AndroidManifest.xml文件。
res是uncompiled resources。
META-INF是签名文件夹。

META-INF其中有三个文件:

  • CERT.RSA
  • CERT.SF
  • MANIFEST.MF

MANIFEST.MF文件
版本号以及每一个文件的哈希值(BASE64)。包括资源文件。这个是对每个文件的整体进行SHA1(hash)。

Manifest-Version: 1.0
Built-By: Generated-by-ADT
Created-By: Android Gradle 2.2.0
Name: res/drawable-xhdpi-v4/abc_scrubber_control_to_pressed_mtrl_005.png
SHA1-Digest: I9s6aQ5VyOLrNo4odqSij549Oyo=
Name: res/drawable-mdpi-v4/abc_textfield_search_default_mtrl_alpha.9.png
SHA1-Digest: D6dilO+UMcglambujyMOhNbLZuY=
……

CERT.SF
这个是对每个文件的头3行进行SHA1 hash。

Signature-Version: 1.0
X-Android-APK-Signed: 2
SHA1-Digest-Manifest: QxOfCCAuQtZnHh0YRNnoxmiHT80=
Created-By: 1.0 (Android)
Name: res/drawable-xhdpi-v4/abc_scrubber_control_to_pressed_mtrl_005.png
SHA1-Digest: I9s6aQ5VyOLrNo4odqSij549Oyo=
Name: res/drawable-mdpi-v4/abc_textfield_search_default_mtrl_alpha.9.png
SHA1-Digest: D6dilO+UMcglambujyMOhNbLZuY=
……

CERT.RSA
这个文件保存了签名和公钥证书。

插件化中资源冲突解决

如果需要宿主、插件之间使用同一套资源管理器,那么我们需要将插件的资源路径添加到宿主的AssetManager中。

我们知道,apk包括代码和资源,在apk编译过程中,dex工具将代码打包成.dex文件,资源文件会由aapt工具生成对应的ID,aapt在打包的时候组织成resources.arsc文件,resources.arsc文件是用来描述资源ID和资源位置配置信息,从18个维度描述了一个资源ID的配置信息(语言、分辨率等),就是资源ID和资源的索引表。资源的ID生成是有规则的,规则:0xPPTTNNNN,由8位16进制组成,其中:
PP段:表示资源的包空间:0x01表示系统资源空间,0x7f表示应用资源空间。
TT段:表示资源类型。
NNNN段:4个16进制表示资源id,一个apk中同一类型资源从0000开始递增。
例如:

nt anim pop_dialog_in 0x7f040000
int anim pop_dialog_out 0x7f040001
int anim slide_left_in 0x7f040002
int anim slide_left_out 0x7f040003
int anim slide_right_in 0x7f040004
int anim slide_right_out 0x7f040005
int anim update_loading_progressbar_anim 0x7f040006
int array indicator_tab_icon 0x7f050001
int array indicator_tab_titlt 0x7f050000

现在问题来了,宿主apk和插件apk是独立编译出来的两个独立的apk,那么其中就有资源ID相同的情况出现,从而产生资源ID冲突。如何解决这个问题?看了一些开源框架,解决的办法就是修改资源ID的PP段,大体有两种做法:

  1. 修改aapt源码,定制aapt工具编译期间修改PP段。
    DynamicAPK的做法就是如此,定制aapt,替换google的原始aapt,在编译的时候可以传入参数修改PP段:例如传入0x05编译得到的资源的PP段就是0x05。个人觉得这个做法不是太灵活,入侵了原有的开发编译流程,不好维护。
  2. 修改aapt的产物,即,编译后期重新整理插件Apk的资源,编排ID。
    前面说过apk编译之后会生成ID以及对应的索引表resorce.arsc,那么我们能不能后期修改相关ID及索引表呢?答案是肯定的,个人比较赞同这种思路,不用入侵原有编译流程。

插件可能是 Apk 也可能是 so 格式,不管哪一种,都不会生成 R.id ,从而没办法使用。这个问题有好几种解决方案。一种是是重写 Context 的 getAsset 、 getResource 之类的方法,偷换概念,让插件读取插件里的资源,但缺点就是宿主和插件的资源 id 会冲突,需要重写 AAPT 。另一种是重写 AMS中保存的插件列表,从而让宿主和插件分别去加载各自的资源而不会冲突。第三种方法,就是打包后,执行一个脚本,修改生成包中资源id。

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

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

相关文章

C++日期类,详细!!!

日期类的练习可以巩固提高之前所学习的知识&#xff0c;还可以完成关于的日期练习 下面是关于日期的对应oj题 KY111 日期差值 计算一年的第几天 累加天数 1.Date.h 头文件部分 看看我们要实现那些接口 1.获取对应月份的天数:GetMonthDay 2. < > …

leetcode41. 缺失的第一个正数,原地哈希表

leetcode41. 缺失的第一个正数 给你一个未排序的整数数组 nums &#xff0c;请你找出其中没有出现的最小的正整数。 请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,0] 输出&#xff1a;3 解释&#xf…

计算机三级网络技术总结(五)

HTTP端口号为80 三平台一出口&#xff1a;网络平台、业务平台、管理平台和城市宽带出口IEEE802.16最高传输速率为134Mbps链路状态数据库中保存的是全网的拓扑结构图&#xff0c;而非全网完整的路由表在无线局域网中&#xff0c;客户端设备用来访问接入点&#xff08;AP&#xf…

GUI编程17:下拉框、列表框

视频链接&#xff1a;19、下拉框、列表框_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1DJ411B75F?p19&vd_sourceb5775c3a4ea16a5306db9c7c1c1486b5 1.下拉框 代码示例 package com.yundait.lesson06;import javax.swing.*; import java.awt.*;public class Te…

计算机毕业设计 教师科研信息管理系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

利用条件编译解决vivado下verilog代码中ila与仿真的共存问题

vivado自带的仿真工具已经接近Modelsim的功能&#xff0c;且与原生开发环境的紧密结合&#xff0c;对仿真非常方便。 我的习惯是在实现工程中另外建一个仿真工程&#xff0c;保存仿真的testbench文件等&#xff0c;而实现工程中保存实际功能的源码文件。 这样仿真时会存在一个问…

目前人工智能时代,程序员如何保持核心竞争力?

随着AIGC&#xff08;如chatgpt、midjourney、claude等&#xff09;大语言模型接二连三的涌现&#xff0c;AI辅助编程工具日益普及&#xff0c;程序员的工作方式正在发生深刻变革。有人担心AI可能取代部分编程工作&#xff0c;也有人认为AI是提高效率的得力助手。面对这一趋势,…

[2025]医院健康陪诊系统(源码+定制+服务)

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

Linux新增用户,对用户提权

文章目录 一、创建用户二、删除用户三、对用户进行提权 一、创建用户 adduser进行创建用户&#xff0c;名字最好不用和指令名称相同。 在创建完用户时最好使用sudo passwd username进行对用户密码的修改. 二、删除用户 userdel进行对用户的删除 三、对用户进行提权 新建用…

Java-面向对象编程(基础部分)

类和对象的区别和联系 类&#xff1a;类是封装对象的属性和行为的载体&#xff0c;在Java语言中对象的属性以成员变量的形式存在&#xff0c;而对象的方法以成员方法的形式存在。 对象&#xff1a;Java是面向对象的程序设计语言&#xff0c;对象是由类抽象出来的&#xff0c;…

手写Spring第三篇,原来Spring容器是使用反射来初始化对象的

上次是不是你小子和大家说你拿来做登记的样品被我收了&#xff0c;然后取豆子的时候就是这个样品的&#xff1f; 今天我来辟一下谣&#xff0c;真的是这样的。这小子的样品确实被我收了&#xff0c;不过这小子没给真东西给我&#xff0c;只给了一个指针&#xff0c;害我宝贝得存…

利士策分享,财富与肥胖:一场关于生活方式的深度对话

利士策分享&#xff0c;财富与肥胖&#xff1a;一场关于生活方式的深度对话 在这个物质充裕、信息爆炸的时代&#xff0c;我们不禁会思考起一些看似不相关实则微妙相连的社会现象&#xff0c; 比如财富与肥胖之间的关系。 这不仅仅是一个关于体重增减的简单议题&#xff0c;更…

自动驾驶中的决策规划技术分享--轻舟智航

文章目录 0.概述&#xff1a;1 导航模块2 决策模块2.1 车道决策2.2 障碍物决策 3 轨迹规划3.1 时空分离规划3.2 时空联合规划 4 对比 0.概述&#xff1a; 李仁杰&#xff0c;轻舟智航规划算法负责人&#xff0c;自动驾驶决策与规划技术专家。 在自动驾驶系统中&#xff0c;决策…

基于SSM的宿舍管理系统的设计与实现 (含源码+sql+视频导入教程+文档+PPT)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1 、功能描述 基于SSM的宿舍管理系统9拥有两种角色&#xff1a;管理员和用户 管理员&#xff1a;宿舍管理、学生管理、水电费管理、报修管理、访客管理、各种信息统计报表 用户&#xff1a;个人信息管…

python mysql pymysql 数据库操作,常用脚本,个人小工具

起因&#xff0c; 目的: 整理 mysql 工具 启动数据库 检查服务器是否启动了: Get-Service -Name ‘mysql*’ 如果没启动的话&#xff0c;那么就启动: net start MySQL80 (最好是开启管理员权限) 1, 日常最常用的&#xff0c;创建连接 --> 查看所有数据库 —> 查看所有…

Docker 部署 sqlserver数据库,并且还原bak文件,使用MSSM 连接 sqlserver

文章目录 1 docker 安装mssql2 使用MSSM连接sqlserver3 还原bak文件 1 docker 安装mssql 拉取镜像 docker pull mcr.microsoft.com/mssql/server:2019-latest运行镜像生成容器 docker run -e "ACCEPT_EULAY" -e "MSSQL_SA_PASSWORDYourPassword123" -p 143…

CSS入门笔记

目录 概述 组成 CSS 语法 常见的使用方式 CSS 优先级 CSS 选择器 1. 基本选择器 2. 属性选择器 3. 伪类选择器 4. 组合选择器 示例 优先级 边框样式与盒子模型 单个边框 边框轮廓&#xff08;Outline&#xff09; 盒子模型 模型介绍 边距设置 布局示例 文…

一个基于VB的期刊信息管理系统

一个基本的期刊信息管理系统的示例&#xff0c;使用 Visual Basic (VB.NET) 编写。这个示例将展示如何创建一个简单的期刊信息管理系统&#xff0c;其中包括添加、查看、编辑和删除期刊的功能。 系统需求 添加期刊&#xff1a;允许用户输入期刊的信息&#xff08;如标题、作者…

如何使用 maxwell 同步到 redis?

文章目录 1、MaxwellListener2、MxwObject1. 使用Maxwell捕获MySQL变更2. 将Maxwell的输出连接到消息系统3. 从消息系统读取数据并同步到Redis注意事项 1、MaxwellListener package com.atguigu.tingshu.album.listener;import com.alibaba.fastjson.JSON; import org.apache.…

文心一言 VS 讯飞星火 VS chatgpt (350)-- 算法导论24.1 1题

一、在图 24-4上运行Bellman-Ford算法&#xff0c;使用结点 z z z作为源结点。在每一遍松弛过程中&#xff0c;以图中相同的次序对每条边进行松弛&#xff0c;给出每遍松弛操作后的 d d d值和 π π π值。然后&#xff0c;把边 ( z , x ) (z,x) (z,x)的权重改为 4 4 4&#xf…