首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Android 构建过程分析

Android 构建过程分析

原创
作者头像
QQ音乐技术团队
修改于 2017-11-08 01:46:09
修改于 2017-11-08 01:46:09
3.4K12
举报

前言

或许我们都知道Android构建会经历资源合并打包、源码编译、dex生成及打包签名等步骤,可是不是每个人对这些过程中发生的事情都了然于心呢?或许不然,于是带着好奇心开始了对Android gradle plugin的学习,现简要整理一下。

资源合并

如果项目引入了android support包,又或许依赖于其它第三方aar库,那构建前会将aar解压并与本地资源合并,这里的资源主要包括assets目录,res目录及Androidmanifest.xml。

当第三方依赖中的assets或res文件与本地文件有冲突时,会优先选用本地文件。但res/values略有不同,此目录下的strings.xml、color.xml、styles.xml等文件会被整合到一个叫values.xml的文件中去,后与各第三方依赖中的values.xml进行内容上的合并,不会像res其它子目录文件一样直接舍弃第三方冲突文件。

Androidmanifest.xml的合并相比来说则要复杂一些,除了第三方依赖中的manifest,项目还可以在不同目录下分别拥有manifest文件。构建过程中,会根据manifest中元素、属性及赋值来生成一个manifest文件,并应用于后续的打包过程。gradle为不同的manifest赋予了不同的优先级,其顺序如下:

buildType 设置 > productFlavor 设置 > src/main > dependency&library

XML元素及属性的冲突会根据以下规则进行解决:

当然也会有一些例外的:

  • uses-feature android:required与uses-library android:required默认为true,根据or规则合并;
  • 如未指定uses-sdk,minSdkVersion跟targetSdkVersion将被设置为1。而冲突时会使用高优化级的设置;
  • 若library的minSdkVersion高于src/main的设置,则会引发error,但可通过overrideLibrary解决。若未指定targetSdkVersion,则其值与minSdkVersion一致;
  • 若library的targetSdkVersion低于src/main的设置,需要添加一些额外的权限保证library能正常运行;
  • manifest元素只与子manifest元素合并;
  • intent-filter元素在合并中不会被改变,只会被添加到其父节点中去;

冲突发生时,可通过合并冲突标记进行解决,需要引入android tools命名空间,详情请参阅官方文档。另外,manifest在对文件进行合并后,还会根据build.gradle的设置覆盖相关属性。

AAPT打包

资源合并后,即进入到编译阶段,先会把项目资源中的xml编译成二进制并生成R.java及资源索引表resources.arsc,其流程如下:

由图可见,assets是不需要做任何处理的,res/raw只需分配id后与assets一起直接打包到应用程序中;基于下述原因,其它xml文件则会被编译成二进制。

  • 编译过程中,会把xml中的字符串进行收集去重,形成字符串资源池,元素中用到字符串的地方将被替换成相应的索引。另外,标签属性/値都会转换为资源id,进一步减少文件大小;
  • 二进制格式的xml把标签属性/値转换为资源id后,避免了字符串解析,从而提高了解析速度;

经过AAPT(Android Asset Packaging Tool)处理后,会输出2个文件:一个R.java,为项目各资源分配了不同的id,将和java源码一起参与到后续的编译过程,id为4字节无符号整数,最高字节表示package id,次高字节表示type id,后2字节表示资源在当前类型中出现的序号,如R.string.appname=0x7f07006b中的0x7f代表当前正在编译的资源包,0x07代表string类型,0x006b代表app_name在string类型中出现的序号;另一个为app.ap,实际上为一个压缩包,包含了assets、res、Androidmanifest.xml与resources.arsc

资源索引表resources.arsc记录了从资源id到文件路径的转换关系,当应用通过Resources类获取res文件资源时,会先从resources.arsc中拿到文件路径,然后通过AssetManager进行访问。

Application Component -> resources.arsc -> AssetManager -> apk

从上述流程中可以看到,若要进行资源的混淆,可在分析resources.arsc格式后,修改内容中文件路径的指向并对资源文件进行相应的重命名即可。

另外,AAPT还可对png图进行优化、指定文件以stored还是deflated模式添加到压缩包中等操作。

源码编译

当项目中包含aidl时,会先调用aidl工具生成java代码;renderscript亦然,需要先调用llvm-rs-cc,只是它不仅会自动生成java文件,还会产生相应的.bc文件,.bc文件将打包到apk中

至此,java代码都已准备完毕。下一步要进行的是通过javac命令将java源码编译成.class字节码,用以编译的classpath包含以下内容:

  • android.jar,具体版本由targetSdkVersion指定;
  • build.gradle中添加的第三方依赖;

编译后可对代码进行混淆处理,主要包括删除无用类、字节码优化、重命名等操作,只需在build.gradle中配置混淆规则即可

代码语言:txt
AI代码解释
复制
buildTypes {
    release {
        minifyEnabled true
        proguardFiles getDefaultProguardFile('proguard-android.txt')
        proguardFile 'proguard/proguard-rules.pro'
    }
}

生成dex

如果项目涉及分dex,那在调用dx命令前,需要做一些准备的工作,把编译后的class文件打包成jar包allclasses.jar,然后生成主dex中必须包含的文件列表。主要包括collect、shrink及create 3个步骤。

首先会通过Androidmanifest.xml过滤出项目中使用到的四大组件(Activity、Service、receiver、provider)、Application及Instrumentation,并写入manifest_keep.txt文件,这些都是会默认添加到主dex的,无须手动设置。除此之外,默认添加的还有继承于 BackupAgent 及 Annotation 的类。若有额外的类需要被加入到主dex中,可以新建一个文件并以proguard的语法指定,然后在build.gradle中把此文件配置到multiDexKeepProguard中去。此过程关键代码如下:

代码语言:txt
AI代码解释
复制
void generateKeepListFromManifest() {
    SAXParser parser = SAXParserFactory.newInstance().newSAXParser()
    Writer out = new BufferedWriter(new FileWriter(getOutputFile()))    try {
        parser.parse(getManifest(), new ManifestHandler(out))        // add a couple of rules that cannot be easily parsed from the manifest.
        out.write("""-keep public class * extends android.app.backup.BackupAgent {<init>();}
-keep public class * extends java.lang.annotation.Annotation {*;}
""")        if (proguardFile != null) {
            out.write(Files.toString(proguardFile, Charsets.UTF_8))
        }
    } finally {
        out.close()
    }
}

这个时候,会执行一个叫shrinkXxxMultiDexComponents(Xxx为build types名称)的任务。实际上是调用了proguard,只是要比常规的proguard简单一些,不执行混淆、优化跟预检几个步骤,只需要shrink即可,以allclasses.jar为输入、manifest_keep.txt为混淆配置文件,把指定内容及其引用标记起来,然后添加到componentClasses.jar中去。

代码语言:txt
AI代码解释
复制
public void execute(ProGuardTask proguardComponentsTask) {
    proguardComponentsTask.dontobfuscate();
    proguardComponentsTask.dontoptimize();
    proguardComponentsTask.dontpreverify();    // 方法未完,略过...}

到了CreateMainDexList,会调用dx命令,传入allclasses.jar、componentClasses.jar,分析后者依赖,把它直接引用的类也添加到主dex中,并生成新的multidex配置文件maindexlist.txt,至此,准备工作完成。

经过上一阶段编译的处理,已经生成了标准的java字节码,可在标准的java虚拟机上运行。但android使用了它特有的dalvik虚拟机,这就需要我们为它提供另一不同的格式。dx工具为此而出现,可将.classes文件转换添加到dalvik可执行文件.dex中去。当项目发展到一定规模,需要进行分dex处理时,可通过上述步骤生成的maindexlist.txt指定dex该如何拆分。

遗憾的是,以上关于分dex的内容都是理想的情况,现实却很残酷。如果项目中开启了proguard,那它会在分dex的shrink处理前完成,导致allclasses.jar是混淆处理后的代码,而manifest_keep.txt却未曾混淆,后续生成componentClasses.jar 及 maindexlist.txt 的过程也就都不再可靠了。要解决这个问题,在shrink前通过混淆输出的符号表mapping.txt对manifest_keep.txt进行修正是个不错的选择。

打包签名

此时万事俱备,只要把资源包app.ap_、可执行文件classes.dex及项目(包含第三方依赖)中的非源码文件一起添加到压缩包中去,我们的安装包(.apk文件)也就生成了。

另外,apk需要经过签名才可以发布。可通过jarsigner工具完成。

zipalign

文件对齐并非android构建的必要步骤,但对齐处理后可提高系统访问安装包资源的效率。即使执行了zipalign,也只有以stored模式添加到apk中的文件是需要对齐的。如若对图片等资源进行了极限压缩或在aapt打包时选择了deflated,那可对齐的文件也就没多少了

通过build tools中的zipalign工具以下命令可对压缩包进行对齐

zipalign -f -v 4 app.apk toapp.apk

以下命令则起到了检验压缩包有没有对齐的作用:

zipalign -c -v 4 app.apk

总结

本文主要介绍了android构建的各个主要步骤,并重点讲述了资源合并打包与dex生成的过程。最后,用一张图概括下构建的总体流程:

以上内容皆基于Android gradle plugin 1.3.0,新版本插件实现略有差异。若有错漏,望不吝赐教~

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
12 条评论
热度
最新
最后的图不错,挺清楚的
最后的图不错,挺清楚的
回复回复点赞举报
压缩包对齐真的舒服,就和代码规范一样,我觉得可以都注意一下
压缩包对齐真的舒服,就和代码规范一样,我觉得可以都注意一下
回复回复点赞举报
文件对其好评
文件对其好评
回复回复点赞举报
还是感觉不够细,真要把这整个过程讲明白了一篇文章肯定是不够的
还是感觉不够细,真要把这整个过程讲明白了一篇文章肯定是不够的
回复回复点赞举报
介绍的挺全面的
介绍的挺全面的
回复回复点赞举报
QQ音乐app就是这么做的吗:D
QQ音乐app就是这么做的吗:D
回复回复点赞举报
谢谢
谢谢
回复回复点赞举报
学这个也没什么必要吧,不是都打包好了,直接用就行
学这个也没什么必要吧,不是都打包好了,直接用就行
回复回复点赞举报
还适用于旧版本吗?
还适用于旧版本吗?
回复回复点赞举报
希望能在具体讲一下。
希望能在具体讲一下。
回复回复点赞举报
加载更多
推荐阅读
编辑精选文章
换一批
Android 项目构建流程
当我们打开一个项目,我们可以看到的是我们写的Java Code文件or Other JVM Code,资源文件,Build配置文件,但是通过run the project,我们就可以得到一个在我们的Andoid设备上可以运行的Apk,上线应用市场,还需要我们对其进行签名处理,来确保我们App的唯一性和安全性。整个过程就是所谓的项目构建。
李林LiLin
2020/12/27
1.5K0
Android插件化基础3----Android的编译打包流程详解
.apk文件其实就是一个压缩包,把文件的后缀改成.zip,用压缩软件解压搜就可的下图(我是mac)
隔壁老李头
2018/08/30
2.3K0
Android插件化基础3----Android的编译打包流程详解
解决插件化资源id冲突
第一步:aapt。为res目录下的资源生成R.java文件,同时为AndroidManifest.xml生成Manifest.java文件
用户3112896
2019/09/26
3.1K0
解决插件化资源id冲突
【Android 安装包优化】资源混淆 ( AAPT2 资源编译工具 | resources.arsc 资源映射表 工作机制 )
文章目录 一、AAPT2 资源编译工具 二、resources.arsc 资源映射表 工作机制 三、参考资料 一、AAPT2 资源编译工具 ---- 资源的编译 , 生成 R.java 文件 , 都是
韩曙亮
2023/03/29
1.2K0
【Android 安装包优化】资源混淆 ( AAPT2 资源编译工具 | resources.arsc 资源映射表 工作机制 )
Android App瘦身实战
随着业务的快速迭代增长,不断引入新的业务逻辑代码、图片资源和第三方SDK等,很多app都面临一个一个结果,app越来越大,甚至很多无用的代码,包体积的增大带来了很多问题,诸如app启动更慢,代码维护越
xiangzhihong
2018/02/06
1.9K0
Android App瘦身实战
深入探索 Android 包瘦身(中)
作者:jsonchao 链接:https://juejin.im/post/5e7ad1c0e51d450edc0cf053
陈宇明
2020/12/16
1.8K0
深入探索 Android 包瘦身(中)
Android App包瘦身优化实践
随着业务的快速迭代增长,美团App里不断引入新的业务逻辑代码、图片资源和第三方SDK,直接导致APK体积不断增长。包体积增长带来的问题越来越多,如CDN流量费用增加、用户安装成功率降低,甚至可能会影响用户的留存率。APK的瘦身已经是不得不考虑的事情。在尝试瘦身的过程中,我们借鉴了很多业界其他公司提供的方案,同时也针对自身特点,发现了一些新的技巧。本文将对其中的一些做详细介绍。 在开始讲瘦身技巧之前,先来讲一下APK的构成。 APK的构成 可以用Zip工具打开APK查看。比如,美团App 7.8.6的线上版本
美团技术团队
2018/03/12
1.7K0
Android App包瘦身优化实践
Android APK编译流程
apk 是Android Package的简写, 在平时的开发过程中,通过点击Run app 按钮 或者 在命令行中输入
艳龙
2021/12/16
2.3K0
Android APK编译流程
Android 混淆那些事儿
本文主要讲述了代码混淆和资源混淆的原理,Studio默认的混淆方案,混淆的参数,以及如何对Apk进行代码混淆(自定义混淆文件)和资源混淆(结合微信混淆和美团混淆两种方案),避免Apk被逆向。 为什么要混淆 我们的apk在打包发布之前,都要进行混淆处理来避免源代码和资源文件被小白用户通过反编译拿到。未混淆代码的反编译操作非常简单,网上有很多教程, 也可以通过使用Android Studio自带的apk分析工具(Build—-Analyze APK)直接看到未混淆Apk的源代码和原始的资源文件。对比图如下,从
腾讯Bugly
2018/03/23
3.4K0
实现Android APK瘦身99.99%
让我们将这一原则应用到 Android App 开发中。我们将玩转一个称为“ApkGolf”的 APK,目的是创建一个尽可能具有最少字节数的 App,并可安装在运行 Oreo 的设备上。
Android技术干货分享
2019/06/13
2.1K0
「万物生长」一个APK从诞生到活跃在Android手机上
上述之前在其他文章里面也常见的图,而这张图讲述一个APK的诞生流程,可以分为以下的几个流程
ClericYi
2020/10/09
1.2K0
「万物生长」一个APK从诞生到活跃在Android手机上
❤️Android 应用的诞生 ❤️ 只需两幅图
在分析安装过程之前,需要先了解一下 Android 项目是如何经过编译->打包生成最终的 .apk 格式的安装包。谷歌有一张官方图片来描述 apk 的打包流程,如下图所示。
Android 帅次
2021/10/20
1.3K0
❤️Android  应用的诞生 ❤️ 只需两幅图
京东金融Android瘦身探索与实践
Tech 导读 随着业务不断迭代更新,京东金融App(Android版本)的体积也在快速增加,2019年~2022年期间甚至一度超过了117M。2022年9月开始针对金融App进行了瘦身专项整治,最终实现从117M瘦身至74M。本文阐述了整个安装包瘦身过程中遇到的问题以及积累的经验,并详细介绍了具体的解决路径。
京东技术
2023/08/22
6830
京东金融Android瘦身探索与实践
Android资源混淆打包方案
概述 我们知道在Android的打包过程中,有一个步骤是压缩,也是为了减少apk包的大小,其中在压缩的过程中,很大一部分就是对资源的压缩,除了系统的压缩方案之外,我们今天讲另外两种压缩方案:微信方案和美团方案 微信的方案是通过修改aapt在处理资源文件相关的源码达到资源文件的替换;而后者指通过直接修改resources.arsc文件达到资源文件混淆的目的。相比之下,微信的方案更加优秀。 微信资源混淆方案 微信中的资源混淆工具主要为了混淆资源ID长度(例如将res/drawable/welcome.png混淆
xiangzhihong
2018/01/26
2.7K0
Android减包 - 减少APK大小
本文是对Google官方文档 Reduce APK Size 的翻译 用户经常会避免下载看起来体积较大的应用,特别是在不稳定的2G、3G网络或者在以字节付费的网络。这篇文章描述了怎样减少你的APK大小,这会让更多的用户愿意下载你的应用。 理解APK的结构 在讨论怎样减少应用大小之前,先了解APK的结构是有用的。一个APK文件就是ZIP包,其中包含了组成你的应用的所有文件,比如Java类文件,资源文件,和一个包含被编译资源的文件。 一个APK包含了以下目录: META-INF/: 包含CERT.SF和CERT
天天P图攻城狮
2018/02/02
2K0
Android减包 - 减少APK大小
Android性能优化(十)之App瘦身攻略
如果你对App优化比较敏感,那么Apk安装包的大小就一定不会忽视。关于瘦身的原因,大概有以下几个方面:
用户2898788
2018/08/21
1.9K0
Android性能优化(十)之App瘦身攻略
聊聊Android编译流程
看起来我们貌似已经回答出了这个问题的答案,但是今天是来屠龙的,所以我们不能就这么简单的放过这个题目。
逮虾户
2020/10/15
2.1K0
聊聊Android编译流程
一文了解Android游戏SDK开发
SDK(Software Development Kit)是软件开发工具包的缩写,一般来说,SDK是用于给开发人员提供进行应用程序开发的工具的,这样程序员就可以快速的开发出应用软件,省去了编写硬件代码和基础代码框架的过程,我们常见的Android SDK就属于这一类。除了这种比较大的SDK,我们平时开发的library也属性SDK,只不过功能比较单一,适用的场合也比较简单,如短视频SDK、推送SDK,分享SDK等。 而我们所做的游戏SDK主要是用于第三方游戏开发接入我们的账号体系和支付体系,类似于友盟分享等聚合SDK。
xiangzhihong
2020/07/15
2.8K0
一文了解Android游戏SDK开发
Android安装包相关知识汇总
用户抱怨安装包越来越大?印度友人反馈装不上微信?欢迎来到本期的走进科学--安装包速成记。做一个有节操的安装包,我们希望它越小越好,并且确保用户都能安装的上。 Android的安装包,简单来说就是一个压缩包,首先我们了解一下它的生成过程。 一、安装包编译过程 一般我们使用ant、gradle等方式编译生成安装包,它一般包含以下几个步骤。 但是对于多library的结构下,ant与gradle的并行度并不足够。当前微信已经切换到Facebook的开源编译工具buck(相关介绍http://facebook.
微信终端开发团队
2018/01/29
1.3K0
Android安装包相关知识汇总
Android的打包过程与资源分配
最近这个版本需要降低APK的大小, 所以很多功能需要从主APK中移除到插件中,除了相关工程的代码,还有Assets、Libs、Resources都需要移动到插件中,而在插件拆分的过程中也遇到了很多问题,需要记录一下。
None_Ling
2018/12/21
1.2K0
相关推荐
Android 项目构建流程
更多 >
领券
一站式MCP教程库,解锁AI应用新玩法
涵盖代码开发、场景应用、自动测试全流程,助你从零构建专属AI助手
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档