专栏首页逮虾户聊聊Android编译流程

聊聊Android编译流程

各位大佬,能不能随便给我的项目或者之前的文章点个star,苦兮兮。github.com/ 掘金文章

面试官:哟,大叔,又来了啊。

我:emmmmm,我今天就是来屠龙了。

面试官:勇气可嘉,那么我们聊聊Android编译流程吧。

我:吃我一招天打雷劈屠真龙。

是时候表演真正的技术了

正常情况下,编译流程都是从下图说起的。

  1. 通过 aapt 打包 res 资源文件,生成 R.java、resources.arsc 和 res 文件(二进制 & 非二进制如 res/raw 和 pic 保持原样);
  2. 处理 .aidl 文件,生成对应的 Java 接口文件;
  3. 通过 Java Compiler 编译 R.java、Java 接口文件、Java 源文件,生成 .class 文件;
  4. 通过 dex 命令,将 .class 文件和第三方库中的 .class 文件处理生成 classes.dex;
  5. 通过 apkbuilder 工具,将 aapt 生成的 resources.arsc 和 res 文件、assets 文件和 classes.dex 一起打包生成 apk;
  6. 通过 Jarsigner 工具,对上面的 apk 进行 debug 或 release 签名;
  7. 通过 zipalign 工具,将签名后的 apk 进行对齐处理。

看起来我们貌似已经回答出了这个问题的答案,但是今天是来屠龙的,所以我们不能就这么简单的放过这个题目。

从gradle Task看编译流程

先贴一段gradle打印task耗时的代码

  1. 项目根目录build.gradle打开
  2. 加入下面代码
import java.util.concurrent.TimeUnit
// Log timings per task.
class TimingsListener implements TaskExecutionListener, BuildListener {
    private long startTime
    private timings = []

    @Override
    void beforeExecute(Task task) {
        startTime = System.nanoTime()
    }

    @Override
    void afterExecute(Task task, TaskState taskState) {
        def ms = TimeUnit.MILLISECONDS.convert(System.nanoTime() - startTime, TimeUnit.NANOSECONDS);
        timings.add([ms, task.path])
        task.project.logger.warn "${task.path} took ${ms}ms"
    }

    @Override
    void buildFinished(BuildResult result) {
        println "Task timings:"
        for (timing in timings) {
            if (timing[0] >= 50) {
                printf "%7sms  %s\n", timing
            }
        }
    }

    @Override
    void buildStarted(Gradle gradle) {}

    @Override
    void projectsEvaluated(Gradle gradle) {}

    @Override
    void projectsLoaded(Gradle gradle) {}

    @Override
    void settingsEvaluated(Settings settings) {}
}

gradle.addListener new TimingsListener()

当项目运行完之后会输出类似如下的日志,表示一个run执行之后gradle所执行的task的时间以及任务名。

  1543ms  :compiler:kaptGenerateStubsKotlin
    144ms  :RouterLib:packageDebugResources
   1166ms  :compiler:kaptKotlin
    816ms  :compiler:compileKotlin
    401ms  :compiler:compileJava
     65ms  :compiler:jar
    122ms  :app:mergeDebugResources
     56ms  :EmptyLoader:compileJava
    170ms  :app:processDebugManifest
    171ms  :RouterLib:parseDebugLocalResources
     60ms  :app:checkDebugDuplicateClasses
   2416ms  :RouterLib:compileDebugKotlin
    122ms  :RouterLib:compileDebugJavaWithJavac
    124ms  :secondmoudle:mergeDebugNativeLibs
   1185ms  :app:processDebugResources
     70ms  :secondmoudle:kaptGenerateStubsDebugKotlin
    202ms  :RouterLib:mergeDebugNativeLibs
    350ms  :secondmoudle:kaptDebugKotlin
    158ms  :secondmoudle:compileDebugJavaWithJavac
   1108ms  :app:kaptGenerateStubsDebugKotlin
     91ms  :secondmoudle:bundleLibRuntimeToJarDebug
    129ms  :app:mergeDebugNativeLibs
    430ms  :app:kaptDebugKotlin
   1008ms  :app:compileDebugKotlin
    120ms  :app:compileDebugJavaWithJavac
    265ms  :app:mergeDebugJavaResource
    181ms  :app:transformClassesAndResourcesWithAuto_registerForDebug
   7262ms  :app:dexBuilderDebug
   1308ms  :app:mergeProjectDexDebug
    344ms  :app:packageDebug

从上述Task列表中可以看出,其实最上面这张图所说的编译流程其实并不完整。

kapt和apt

我上篇文章说了,javaCompiler执行之前会先执行apt,生成java代码,其任务名就是kaptGenerateStubsDebugKotlin。

聊聊AbstractProcessor和Java编译流程

compiler 混入了奇怪的东西

kotlin已经被引入了很多版本了,但是kotlin的compiler其实和java compiler是不一样的。

如果按照标准答案去回答这个问题吧,总感觉还是有所欠缺的,所以我们需要补充的一个点就是compileDebugKotlin

当然少不了transform

当我们使用字节码插桩之后其实就增加了个transform的流程,也就是这个transformClassesAndResourcesWithAuto_registerForDebug

那么是不是还有什么可以补充的呢?

AGP在不同版本的差异还是比较大的。特别是在3.2版本之上的版本被引入了D8编译器之后。

低版本先使用DX编译器将class转化为dex。

而高版本采用d8编译器将class转化为dex。

desugar是干嘛的?

Android Studio 为使用部分 Java 8 语言功能及利用这些功能的第三方库提供内置支持。默认工具链对 javac 编译器的输出执行字节码转换(称为 desugar),从而实现新语言功能。

语法糖香归香,但是最后.dex可是不认识你的。

那么D8的优势是什么呢???

话不多,直接上图。

可以看到D8在编译速度以及编译出来的文件体积上有了明显的提升。

那么混淆呢??

看看最一开始的图,有没有发现少了混淆的流程呢!!!

在AGP3.4版本上引入了R8,也就是混淆升级版本。而且在高版本上,整体流程也其实发生了微妙的变更,将原先的流程进行了合并。

  1. R8开启前的编译流程
  1. R8开启后的编译流程

说句题外话,但是R8更吃内存,机器辣鸡的老哥慎重点。

关于签名

之前写的东西有点遗漏啊,谷歌官方有说明,下面是引用啊

注意:您必须在应用构建过程中的两个特定时间点之一使用 zipalign,具体在哪个时间点使用,取决于您所使用的应用签名工具:

如果您使用的是 apksigner,则只能在为 APK 文件签名之前执行 zipalign。如果您在使用 apksigner 为 APK 签名之后对 APK 做出了进一步更>改,签名便会失效。

如果您使用的是 jarsigner,则只能在为 APK 文件签名之后执行 zipalign。

链接地址

那么当使用V1签名时,编译流程顺序还是6-7

而当使用的是V2的签名时,则编译流程顺序是7-6

结束

其实并没有什么想说的,只是想给各位老哥表演下倒立吹牛逼。觉得还ok 给我点个赞把。万一有哪个倒霉蛋下次再被问到这个题目,可以来这里上个香。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 聊聊AbstractProcessor和Java编译流程

    我:我写过一个路由跳转库,我通过了AbstractProcessor生成了路由表的注册类。

    逮虾户
  • 【连载】聊聊 APK(三) —— Android 资源编译的秘密

    上两期我们讲了 APK 里面 Dex 的东西,明白了 Dex 只是 classes 的某种打包形式,我们暂时不拘泥于细节,关于代码的部分就告一段落。我们知道除了...

    程序亦非猿
  • 【连载】聊聊 APK(四) —— 脱离 AS 手工创造 APK 文件

    想进大厂,就关注「 程序亦非猿 」 时不时 8:38 推送优质文章,觉得有用,置顶加星标

    程序亦非猿
  • Android AAPT1编译流程

    在APP打包过程中 , 会通过AAPT编译资源以及生成R.java文件. 一般我们使用以下命令来调用aapt命令 :

    None_Ling
  • Android APK编译流程

    apk 是Android Package的简写, 在平时的开发过程中,通过点击Run app 按钮 或者 在命令行中输入

    艳龙
  • Android 菜鸟面经总结:基础和算法不好?小心面试扑街警告!

    apt生成的是java的class,而且不能更改原始的类,而transform这个东西你可以为所欲为,但是毕竟操作的是字节码,风险系数更高,面试官还特地问了我下...

    Android技术干货分享
  • 全球开发者报告:1100万开发人员积极使用 JavaScript[每日前端夜话0xEC]

    哪种编程语言拥有最强大的社区和最活跃的程序员?有多少开发人员正在实施 DevOps 策略?移动应用程序员在使用什么框架?让我们来看看 SlashData 的报告...

    疯狂的技术宅
  • 何为SourceMap?从编译聊聊其原理

    问题在于,由于打包动作会将我们的原始代码进行编译、压缩,最后在产物中早已没有我们的原始代码,打开产物,我们可以见到的只有这样的代码:

    Nealyang
  • 【连载】聊聊 APK——直接运行 Dex文件的黑魔法

    想进大厂,就关注「 程序亦非猿 」 时不时 8:38 推送优质文章,觉得有用,置顶加星标

    程序亦非猿
  • 为Android开发者整理的Google I/O开发者大会第一弹

    今天凌晨的Google I/O开发者大会不像以往的历届,貌似今年的人工智能和智能家居抢走了Android系统的风头。以往每年应该都是 Android 新系统的发...

    非著名程序员
  • Android 基础架构组面试以及面试题

    年中的时候帮部门招人,发现很多候选人对于我们部门还是很青睐的。也对鸡架部门做的事比较感兴趣,所以今天这篇水文主要就给大家梳理下基架的面试题以及基础架构组涉及的s...

    程序员小顾
  • 聊一聊少儿编程

    1984年,邓小平的一句话开启了中国计算机的新篇章“计算机普及要从娃娃抓起”。而且在2017年浙江省就明确表明,Python将纳入浙江省的的高考。如果说这离我们...

    算法与编程之美
  • 全开源即时通讯(IM)系统-仿微信

    E聊SDK目标是打造一个免费开源,接入简单,适应多平台的即时通信SDK,为广大开发者提供便利。

    E聊
  • 多人视频直播交友系统——视频直播源码开发的进阶之路

    去年下半年,多人视频直播交友系统开始崛起,随着5G时代到来,它比4G性能更强、支持超高速率和超低延时,网速的加快会带动互联网行业的发展,同时对于直播行业来说在技...

    用户2954023423
  • 在Android项目中使用Java8

    前言 在过去的文章中我介绍过Java8的一些新特性,包括: Java8新特性第1章(Lambda表达式) Java8新特性第2章(接口默认方法) Java8新特...

    张磊BARON
  • 来聊聊Android的多线程

    答:其实除了Activity,其它组件也都在主线程。这就意味着如果在其他组件中做耗时操作的话,同样会另主线程阻塞。

    PhoenixZheng
  • E聊SDK在TypeScript下的条件编译(使用js-conditional-compile-loader插件)

    条件编译: 用同一套代码和同样的编译构建过程,根据设置的条件,选择性地编译指定的代码,从而输出不同程序的过程

    E聊
  • 2019阿里、腾讯、字节跳动Android高级面试题小整理

    找工作还是需要大家不要紧张,有我们干这一行的接触人本来就不多 难免看到面试官会紧张,主要是因为怕面试官问的问题到不上来,那时候不要着急 ,答不上了的千万不然胡扯...

    Android技术干货分享
  • 一对一直播源码,双人一对一视频直播聊天交友系统如何快速实现?

    一对一直播平台指的就是盈利性双人一对一视频直播聊天交友系统,是私密性极强的互动系统,而一对一直播源码则是能够快速实现这种一对一视频直播聊天交友平台搭建的系统程序...

    布谷鸟小刘

扫码关注云+社区

领取腾讯云代金券