从构建工具看 Android APK 编译打包流程(上)

简介: 在Android Studio中,我们几乎每天都在用run,generate APK等功能。

前言


在Android Studio中,我们几乎每天都在用run,generate APK等功能。


那你有没有想过这其中的原理呢?比如编译打包的流程?AAPT是什么?这其中有哪些task会执行?


今天就和大家一起去探索下Android中编译打包的那些事儿。


粗谈构建流程


对于编译打包过程,Android官网上有一张图做了简单介绍:


24.png


图画的比较简单,标出了大体流程,可以发现整个构建流程大概分为两部分:编译(Compile),打包(Package)


  • 编译


编译过程就是将 java文件 编译成 class文件,最后优化成 dex文件


  • 打包


打包流程就是将DEX文件和编译后的资源组合成单个APK,并且签名,生成最终的APK。


在这些工作中会有很多工具来辅助完成,比如AAPT,aidl,javac,apk builder 等等。


那在Android Studio中,又是由谁来调度这些工具的呢?Gradle构建工具。


也就是说,在我们点击 generate APK 之后,Gradle就会执行一系列的约定好的 task,每个task有自己的构建工作,按照编译打包的顺序,分别调用具体的工具,最终组织起了整个构建流程。


Gradle 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(编译aidl文件)


对于AIDL,大家应该都很熟悉,它是一种用于进程间通信的接口文件。


其实它是Google为了帮助我们进行进程间通信的简便写法,最后还是需要被解析编译为java文件,而做这个工作的就是aidl工具,存在于sdk/build-tools目录。


主要的工作就是将项目中的aidl文件编译为java文件


生成BuildConfig文件,资源文件


在引入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


merge 资源文件


这一步就是合并res资源文件,assets文件,manifest文件。


因为在项目中会依赖不同的库、组件,也会有多渠道的需求,所以merge这一步操作就是将不同地方的资源文件进行整合。


多个manifest文件需要整理成一个完整的文件,所以如果有属性冲突这一步就会报错。资源文件也会整理分类到不同的分辨率目录中。


AAPT/AAPT2(打包资源文件)


然后就是打包资源文件,涉及到的工具是AAPT。


AAPT,全称Android Asset Packaging Tool,所以这个构建工具就是用来打包资源文件的。


资源文件包括:图片,res目录下的xml文件,AndroidManifest.xml文件;


处理资源文件主要包括两步:


  • 1、编译:将资源文件编译为二进制格式。


把所有的Android资源文件进行解析,生成扩展名为.flat的二进制文件。比如是png图片,那么就会被压缩处理,采用.png.flat的扩展名。


  • 2、链接:合并所有已编译的文件并打包到一个软件包中。


首先,这一步会生成辅助文件,比如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就会对这种情况进行优化。


目录
相关文章
|
15天前
|
Android开发
我的Android进阶修炼:安卓启动流程之init(1)
本文深入分析了Android系统中的init进程,包括其源码结构、主要功能以及启动流程的详细注解,旨在帮助读者理解init作为用户空间的1号进程在Android启动过程中的关键作用。
20 1
|
14天前
|
Java Android开发 Windows
使用keytool查看Android APK签名
本文介绍了如何使用Windows命令行工具和keytool查看APK的签名信息,并提供了使用AOSP环境中的signapk.jar工具对APK进行系统签名的方法。
35 0
使用keytool查看Android APK签名
|
30天前
|
Android开发
将AAB(Android App Bundle)转换为APK
将AAB(Android App Bundle)转换为APK
31 1
|
30天前
|
Android开发 开发者
Android、Flutter为不同的CPU架构包打包APK(v7a、v8a、x86)
Android、Flutter为不同的CPU架构包打包APK(v7a、v8a、x86)
42 1
|
1月前
|
Android开发
解决android apk安装后出现2个相同的应用图标
解决android apk安装后出现2个相同的应用图标
118 2
|
1月前
|
XML 前端开发 Android开发
Android面试高频知识点(3) 详解Android View的绘制流程
View的绘制和事件处理是两个重要的主题,上一篇《图解 Android事件分发机制》已经把事件的分发机制讲得比较详细了,这一篇是针对View的绘制,View的绘制如果你有所了解,基本分为measure、layout、draw 过程,其中比较难理解就是measure过程,所以本篇文章大幅笔地分析measure过程,相对讲得比较详细,文章也比较长,如果你对View的绘制还不是很懂,对measure过程掌握得不是很深刻,那么耐心点,看完这篇文章,相信你会有所收获的。
65 2
|
2月前
|
Java Android开发
android 设置系统时间的流程
android 设置系统时间的方法
202 2
|
3月前
|
安全 网络协议 算法
Android网络基础面试题之HTTPS的工作流程和原理
HTTPS简述 HTTPS基于TCP 443端口,通过CA证书确保服务器身份,使用DH算法协商对称密钥进行加密通信。流程包括TCP握手、证书验证(公钥解密,哈希对比)和数据加密传输(随机数加密,预主密钥,对称加密)。特点是安全但慢,易受特定攻击,且依赖可信的CA。每次请求可能复用Session ID以减少握手。
45 2
|
2月前
|
消息中间件 前端开发 Android开发
Android面试题自定义View之Window、ViewRootImpl和View的三大流程
Android开发中,View的三大核心流程包括measure(测量)、layout(布局)和draw(绘制)。MeasureSpec类在测量过程中起到关键作用,它结合尺寸大小和模式(EXACTLY、AT_MOST、UNSPECIFIED)来指定View应如何测量。onMeasure方法用于自定义View的测量,布局阶段,ViewGroup调用onLayout确定子元素位置,而draw阶段按照特定顺序绘制背景、内容、子元素和装饰。整个流程始于ViewRootImpl的performTraversals,该方法触发测量、布局和绘制。
35 0
|
2月前
|
Android开发
【亲测,安卓版】快速将网页网址打包成安卓app,一键将网页打包成app,免安装纯绿色版本,快速将网页网址打包成安卓apk
【亲测,安卓版】快速将网页网址打包成安卓app,一键将网页打包成app,免安装纯绿色版本,快速将网页网址打包成安卓apk
58 0