在Android Studio中,我们几乎每天都在用run,generate APK
等功能。那你有没有想过这其中的原理呢?比如编译打包的流程?AAPT是什么?这其中有哪些task会执行?今天就和大家一起去探索下Android中编译打包的那些事儿。
对于编译打包过程,Android官网上有一张图做了简单介绍:
图画的比较简单,标出了大体流程,可以发现整个构建流程大概分为两部分:编译(Compile)
,打包(Package)
编译过程就是将 java文件
编译成 class文件
,最后优化成 dex文件
。
打包流程就是将DEX文件和编译后的资源组合成单个APK,并且签名,生成最终的APK。
在这些工作中会有很多工具来辅助完成,比如AAPT,aidl,javac,apk builder
等等。
那在Android Studio中,又是由谁来调度这些工具的呢?Gradle构建工具。
也就是说,在我们点击 generate APK
之后,Gradle就会执行一系列的约定好的 task,每个task有自己的构建工作,按照编译打包的顺序,分别调用具体的工具,最终组织起了整个构建流程。
在Android Studio中,我们运行一个debug包,Build控制台上就可以看到所有的构建相关task:
Starting Gradle Daemon...
Gradle Daemon started in 902 ms
> Task :app:preBuild UP-TO-DATE
> Task :app:preDebugBuild UP-TO-DATE
> Task :app:compileDebugAidl NO-SOURCE
> Task :app:compileDebugRenderscript NO-SOURCE
> Task :app:generateDebugBuildConfig UP-TO-DATE
> Task :app:checkDebugAarMetadata UP-TO-DATE
> Task :app:generateDebugResValues UP-TO-DATE
> Task :app:generateDebugResources UP-TO-DATE
> Task :app:mergeDebugResources UP-TO-DATE
> Task :app:createDebugCompatibleScreenManifests UP-TO-DATE
> Task :app:extractDeepLinksDebug UP-TO-DATE
> Task :app:processDebugMainManifest
> Task :app:processDebugManifest
> Task :app:javaPreCompileDebug UP-TO-DATE
> Task :app:mergeDebugNativeDebugMetadata NO-SOURCE
> Task :app:mergeDebugShaders UP-TO-DATE
> Task :app:compileDebugShaders NO-SOURCE
> Task :app:generateDebugAssets UP-TO-DATE
> Task :app:mergeDebugAssets UP-TO-DATE
> Task :app:compressDebugAssets UP-TO-DATE
> Task :app:processDebugJavaRes NO-SOURCE
> Task :app:mergeDebugJniLibFolders UP-TO-DATE
> Task :app:mergeDebugNativeLibs UP-TO-DATE
> Task :app:stripDebugDebugSymbols NO-SOURCE
> Task :app:validateSigningDebug UP-TO-DATE
> Task :app:mergeLibDexDebug
> Task :app:processDebugManifestForPackage
> Task :app:processDebugResources
> Task :app:compileDebugKotlin UP-TO-DATE
> Task :app:compileDebugJavaWithJavac UP-TO-DATE
> Task :app:compileDebugSources UP-TO-DATE
> Task :app:mergeDebugJavaResource UP-TO-DATE
> Task :app:dexBuilderDebug
> Task :app:mergeExtDexDebug
> Task :app:mergeProjectDexDebug
> Task :app:packageDebug
> Task :app:assembleDebug
BUILD SUCCESSFUL in 22s
27 actionable tasks: 10 executed, 17 up-to-date
我精简了下,留下了比较重要的task:
//aidl 转换aidl文件为java文件
> Task :app:compileDebugAidl
//生成BuildConfig文件
> Task :app:generateDebugBuildConfig
//获取gradle中配置的资源文件
> Task :app:generateDebugResValues
// merge资源文件
> Task :app:mergeDebugResources
// merge assets文件
> Task :app:mergeDebugAssets
> Task :app:compressDebugAssets
// merge所有的manifest文件
> Task :app:processDebugManifest
//AAPT 生成R文件
> Task :app:processDebugResources
//编译kotlin文件
> Task :app:compileDebugKotlin
//javac 编译java文件
> Task :app:compileDebugJavaWithJavac
//转换class文件为dex文件
> Task :app:dexBuilderDebug
//打包成apk并签名
> Task :app:packageDebug
这里涉及到的代码很多,今天就不详细说了,task主要涉及到的工具和功能我已经标注上了,下面就具体说说编译打包流程
。
(感兴趣的朋友可以自己看看源码,查看gradle源码的方法我也放到文末的附目录了)
对于AIDL
,大家应该都很熟悉,它是一种用于进程间通信的接口文件。
其实它是Google为了帮助我们进行进程间通信的简便写法,最后还是需要被解析编译为java文件,而做这个工作的就是aidl工具,存在于sdk/build-tools目录。
主要的工作就是将项目中的aidl文件编译为java文件
。
在引入Gradle编译工具之后,Apk的打包流程就多了这么一步,生成BuildConfig文件和资源文件
。
也就是会根据build.gradle里面配置的内容生成相应的java代码或者res代码。简单举个例子:
//build.gradle
buildTypes {
debug{
buildConfigField("boolean", "ISDEBUG", "true")
resValue "string", "TestName", "love1"
}
release {
buildConfigField("boolean", "ISDEBUG", "false")
resValue "string", "TestName", "love2"
}
}
//BuildConfig.java
public final class BuildConfig {
// Field from build type: debug
public static final boolean ISDEBUG = true;
}
R.string.TestName
这一步就是合并res资源文件,assets文件,manifest文件。
因为在项目中会依赖不同的库、组件,也会有多渠道的需求,所以merge
这一步操作就是将不同地方的资源文件进行整合。
多个manifest文件需要整理成一个完整的文件,所以如果有属性冲突这一步就会报错。资源文件也会整理分类到不同的分辨率目录中。
然后就是打包资源文件,涉及到的工具是AAPT。
AAPT,全称Android Asset Packaging Tool
,所以这个构建工具就是用来打包资源文件的。
资源文件包括:图片,res目录下的xml文件,AndroidManifest.xml文件;处理资源文件主要包括两步:
编译
:将资源文件编译为二进制格式。把所有的Android资源文件进行解析,生成扩展名为.flat的二进制文件。比如是png图片,那么就会被压缩处理,采用.png.flat的扩展名。
链接
:合并所有已编译的文件并打包到一个软件包中。首先,这一步会生成辅助文件,比如R.java(R文件),R文件大家应该都比较熟悉,就是一个资源索引文件,我们平时引用也都是通过R.的方式引用资源id。
最后,会将R文件和之前的二进制文件进行打包,打包到一个APK压缩包(没有dex文件、没有签名)。
再扩展一个问题,关于AAPT2
。(之前有朋友面试遇到问这个的,真是问的比较细啊,所以这里就提一嘴)
Android Gradle
插件 3.0.0 及更高版本默认情况下会启用 AAPT2,而老版本的AAPT已经被弃用,那么AAPT2到底优化改进了什么呢?
1、链接过程优化
在AAPT中是没有链接功能的,会将所有的资源进行编译生成压缩包。这样处理方式有个缺点就是每次编译都要全量编译。
所以在AAPT2中用到链接的功能,当修改了某个资源文件之后,只需要重新编译这个改变的文件,然后与其他资源进行链接即可,支持了增量更新,大大提升了效率。
2、行为变化
对一些行为进行了优化,一些错误的元素以前不会报错,只会警告或者忽略,现在会直接报错,保证程序正确运行。比如
1)、在以前的AAPT版本,Android 清单文件中出现错误的节点元素只会被忽略或警告,而AAPT2开始会对这些节点进行报错,比如:
<activity android:name=".MainActivity">
<action android:name="android.intent.action.TEST" />
</activity>
AndroidManifest.xml:15: error: unknown element <action> found.
2)、在AAPT2中,无法通过name属性指明资源类型了,需要单独使用type属性:
<item name="attr/my_attr">@color/pink</item>
// 修改为
<item type="attr" name="my_attr">@color/pink</item>
3)、ForegroundLinearLayout(前景色相关)属性限制严格
foregroundInsidePadding属性,不属于android命名空间,所以AAPT2的改进就是对于这个属性使用更加严格了,原来使用android:foregroundInsidePadding的时候会被忽略,现在会报错,需要改为foregroundInsidePadding。
4)、@ 资源引用符号使用严格
对于遗漏或者错误引用@(资源引用符号)时候,AAPT2会报错。
5)、库配置不正确
当某些库创建过程中R文件字段声明为final会导致报错,AAPT2就会对这种情况进行优化。
接下来就是编译java文件了,用到的工具就是大家熟知的javac
,通过它将java文件编译成.class
文件。
注解代码也是在这个阶段生成的。当注解的生命周期被设置为CLASS的时候,就代表该注解会在编译class文件的时候生效,并且存在与java源文件和Class字节码文件。
javac的基础命令还是可以了解下:
javac -d destdir(class文件存放目录) srcFile(java文件)
这一步就是转换.class
文件为dex
文件。
有人就奇怪了,.class文件不就是JVM可以识别的二进制文件吗,为什么还要进行一次转化呢?这就涉及到另一个问题:JVM 和 Dalvik(ART)的区别。
其中一个重要的区别就是Dalvik(ART)
有自己的二进制文件,也就是.dex
文件,所以需要将class文件进行再一次转换。
你可以把dex文件理解为一个class文件包,里面装着很多的class文件,让这些类能够共享数据,类似这种关系:
再谈谈这三个工具(dx/r8/d8)的区别:
dx
是最早的转换工具,用于转换class文件为dex文件。D8
编译器和 R8
工具。注意这里的措辞:D8 编译器和 R8 工具。
所以D8
就是用来代替dx用来进行转换class文件的,它的优势在于:编译更快、更小的dex文件、更好的性能。
而R8
工具是用来替代ProGuard的,用于代码的压缩和混淆。
编译class文件过程也常用于编译插桩,比如ASM,通过直接操作字节码文件完成代码修改或生成。
这一步就是生成APK文件,将manifest文件、resources文件、dex文件、assets文件
等等打包成一个压缩包,也就是apk文件。
在老版本使用的工具是apkbuilder
,但是在最新的版本我发现没有这个工具了,sdk目录下也找不到了。
所以我想到从打包的task——packageDebug
中找找答案,果然,让我找到了新的打包工具——zipflinger
。
//PackageAndroidArtifact.java (packageDebug相关代码)
for (File arch : archives) {
mApkCreator.writeZip(arch, pathNameMap::get, name -> !names.contains(name));
}
mApkCreator =new ApkFlinger(mCreationData, compressionLevel, !mIsDebuggableBuild);
/** An implementation of [ApkCreator] using the zipflinger library */
class ApkFlinger
同时在Android Studio
的更新日志中也找到了对应的说明:
Android 构建团队不断进行更改以提高生成性能,在此版本中(Android Studio 3.6),我们将默认打包工具更改为 zipflinger 以进行调试生成。
zipalign 是一种归档对齐工具,可对 Android 应用 (APK) 文件提供重要的优化
具体来说,它会使 APK 中的所有未压缩数据(例如图片或原始文件)在 4 字节边界上对齐。
这里涉及到一个Data structurealignment
(数据对齐)的知识点,其大概意思就是如果数据是自然对齐的,CPU读写就会更高效。
有的朋友可能会疑惑,这个对齐处理不是应该放在签名之后吗?其实这里就涉及到了签名工具的不同带来的对齐处理的顺序不同:
apksigner
,只能在为 APK 文件签名之前执行 zipalign。jarsigner
,只能在为 APK 文件签名之后执行 zipalign。下面具体聊聊两种签名工具。
在生成APK文件之后,必须对该apk文件进行签名,否则无法被安装。
之前大家比较熟知的签名工具是JDK提供的jarsigner,而apksigner是Google专门为Android提供的签名和签证工具。
其区别就在于jarsigner只能进行v1签名,而apksigner可以进行v2、v3、v4签名。
什么?还有v4?我开始看到的时候也是大吃一惊,没想到都有v4签名了,那就顺带介绍下这几个签名机制吧:
v1签名
v1签名方式主要是利用META-INFO
文件夹中的三个文件。
首先,将apk中除了META-INFO文件夹中的所有文件进行进行摘要写到 META-INFO/MANIFEST.MF;然后计算MANIFEST.MF文件的摘要写到CERT.SF;最后计算CERT.SF的摘要,使用私钥计算签名,将签名和开发者证书写到CERT.RSA。
所以META-INFO文件夹中这三个文件就能保证apk不会被修改。
但是缺点也很明显,META-INFO文件夹不会被签名,所以美团针对这种签名方式设计了一种多渠道打包方案:
利用pythone在META-INFO文件夹中创建一个文件,其名称就是渠道名,然后用java去读取文件名获取渠道。
v2签名
Android7.0
之后,推出了v2签名,为了解决v1签名速度慢以及签名不完整的问题。
apk本质上是一个压缩包,而压缩包文件格式一般分为三块:文件数据区,中央目录结果,中央目录结束节。
而v2要做的就是,在文件中插入一个APK签名分块,位于中央目录部分之前,如下图:
这样处理之后,文件就完成无法修改了。
v3签名
Android 9
推出了v3签名方案,和v2签名方式基本相同,不同的是在v3签名分块中添加了有关受支持的sdk版本和新旧签名信息,可以用作签名替换升级。
v4签名
Android 11
推出了v4签名方案。
v4 签名基于根据 APK 的所有字节计算得出的 Merkle 哈希树。它完全遵循 fs-verity 哈希树的结构,将签名存储在单独的.apk.idsig 文件中。
这里提供一种Gradle
源码的查看方式,就是导入Gradle
库,然后在External Libraries
中查看:
implementation 'com.android.tools.build:gradle:4.1.1'
先以依赖的方式导入gradle库,然后编译,就能在左侧External Libraries栏中看到源码了:
https://developer.android.google.cn/studio/build/index.html https://cloud.tencent.com/developer/article/1392511 https://www.jianshu.com/p/e73510605c56 https://blog.csdn.net/qq_43278826/article/details/86543932
本文由哈喽比特于3年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/cJ3qnlz40SbkSCC-jq_rlg
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。