一篇文章了解Android编译构建 编译优化_android版本不同编译规则是否有变化-程序员宅基地

技术标签: apply changes  编译  dex  android  提高编译速度  

前言

作为一名Android开发,我们总会面对下面这个按钮,对于一些大型项目,或者对于在编译期间做了很多task的工程都会花去相当多的时间。就我而言,经常跑一个debug的包需要3分钟左右,当跑release的包需要10分钟左右,如果是一些性能差的电脑,这个时间会消耗的更多。今天文章的主题有两个部分,一个是Android的编译都做了哪些事,还有一个是如何提高我们的编译是速度以及相关原理。
今天的主题将围绕下面这个按钮开始讲述。
在这里插入图片描述

什么是编译

大家在刚学计算机基础这门课程的时候就知道,“计算机只认识0和1,我们写的编译程序经编译器翻译成由0和1构成的二进制格式才能由计算机执行”。所以,我们写的java语言或者kotlin语言最终都会转化成0,1的二进制数据。在这里,这些二进制数据有一个名字—字节码(各种不同平台的虚拟机与所有平台都统一使用的程序存储格式)。在java虚拟机中,使用java编译器可以把java代码编译为存储字节码的Class文件。Class文件是一组以8位字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑的排列在Class文件中。对于具体的词法分析、语法分析以及底层编译原理就不在这里细讲了。

具体看下下面的图。

在这里插入图片描述

你可以把编译简单理解为,将高级语言转化为机器或者虚拟机所能识别的低级语言的过程。对于 Android 来说,这个过程就是把 Java 或者 Kotlin 转变为 Android 虚拟机运行的Dalvik 字节码(DEX文件)的过程。

java字节码和 Dalvik字节码


javac Sample.java   // 生成Sample.class,也就是Java字节码
//  javac -source 1.8 -target 1.8 Sample.java 也可以通过指定版本与dex对应上
javap -v Sample     // 查看Sample类的Java字节码

//通过Java字节码,生成Dalvik字节码
dx --dex --output=Sample.dex Sample.class   

dexdump -d Sample.dex   // 查看Sample.dex的Dalvik的字节码

//javac .class
  public void test();
    descriptor: ()V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #13                 // String 哈哈哈哈
         5: invokevirtual #15                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 8: 0
        line 9: 8

Dalvik字节码

JVM、DVM(dalvik)和ART

Dalvik与JVM的区别
  1. Java 虚拟机是基于栈实现,而 Android 虚拟机是基于寄存器实现。基于寄存器的虚拟机对于编译后变大的程序来说,在它们执行的时候,花费的时间更短。
  2. Java虚拟机运行java字节码,Dalvik虚拟机运行的是其专有的文件格式Dex
Dalvik与ART的区别
  1. 平台支持区别
     Dalvik:Android 4.4及其以下系统使用。
     ART:Android 4.4以上系统使用。

  2. 工作原理区别

  • Dalvki采用JIT(Just In Time)技术。
    JIT为“即时编译技术”,当App运行时,每当遇到一个新类,JIT编译器就会对这个类进行编译,经过编译后的代码,会被优化成相当精简的原生型指令码(即native code),这样在下次执行到相同逻辑的时候,速度就会更快。
  • ART采用AOT技术。
    预先 (AOT) 编译。在ART 环境中,应用在第一次安装的时候,字节码就会预先编译成机器码,使其成为真正的本地应用。之后打开App的时候,不需要额外的翻译工作,直接使用本地机器码运行,因此运行速度提高。ART需要应用程序在安装时,就把程序代码转换成机器语言,所以这会消耗掉更多的存储空间,但消耗掉空间的增幅通常不会超过应用代码包大小的20%
    由于有了一个转码的过程,所以应用安装时间难免会延长

构建流程

我们经常点击构建按钮,就会看到Android studio下方开始不停的执行task,最终会生成一个APK并打开我们设置的launch Activity。以官方的话来说,
Android 编译系统会编译应用资源和源代码,然后将它们打包成可供您测试、部署、签署和分发的 APK。我们具体可以看下整个构建流程。

图片名称

总共可以分为以下4部分

  • 1.编译器将您的源代码转换成 DEX 文件(Dalvik 可执行文件,其中包括在 Android 设备上运行的字节码),并将其他所有内容转换成编译后的资源。
  • 2.APK 打包器将 DEX 文件和编译后的资源组合成单个 APK。
  • 3.APK 打包器使用调试或发布密钥库为 APK 签名
  • 4.在生成最终 APK 之前,打包器会使用 zipalign 工具对应用进行优化,以减少其在设备上运行时所占用的内存。

在这里插入图片描述

在上文我们了解到我们的java和kotlin代码会被Android虚拟机转化成dex文件,那我们的资源文件以及xml又是如何打包的呢。我们来了解下 AAPT2

AAPT2

AAPT2(Android 资源打包工具)是一种构建工具,Android Studio 和 Android Gradle 插件使用它来编译和打包应用的资源。AAPT2 会解析资源、为资源编制索引,并将资源编译为针对 Android 平台进行过优化的二进制格式。

AAPT2 支持通过启用增量编译实现更快的资源编译。这是通过将资源处理拆分为两个步骤来实现的:

  • 编译:将资源文件编译为二进制格式。
  • 链接:合并所有已编译的文件并将它们打包到一个软件包中。

如何优化编译速度

这里我们来讲讲增量编译相关优化方案

增量编译–对用户源程序局部修改后进行的重新编译的工作只限于修改的部分及与之相关部分的内容。相关部分的确定由编译系统确定,对用户是透明的。增量编译对软件开发,尤其是在调试期,可以大大缩短编译时间, 提高编译效率,这也是增量编译的优势所在。

关于增量编译方案

我们在上文了解了整个apk的构建流程,每次正常打apk都需要完整的链路走过一次,假设我们只改动了部分代码,能不能只编译我们改动的部分,其余的部分利用缓存机制,来帮助我们快速构建apk呢。
我们来看下目前市面上已有的编译方案。

instant-run

Instant Run的作用是使得开发过程中的改动可以不用完整编译并重新安装app就能应用,也就是更快看到改动的实际效果,节省时间。实现的原理是通过修改原先的构建过程在初始编译中实现插桩,在后面的改动中只编译改动的部分,并把产物推送到设备上,并通过植入app中的runtime加载新的变动。

  • 因其代码增量是通过运行期hack method实现,所以进行了instant-run后,实际App没有重新走原有该走的生命周期,导致要看到类似onCreate,onResume等生命周期方法修改后的效果,必须手动重启一次进程,另外因为不同手机指令集合的不同,instant-run还会有一定挂掉的机会。
  • 在 Gradle 4.6 之前,如果项目中运用了 Annotation Processor。为了保证准确性,本次修改以及它依赖的模块都需要全量 javac,而这个过程是非常慢的。

因为 instant-run存在一些弊端,在 Android Studio 3.5 及更高版本中,Apply Changes 可让您将代码和资源更改推送给正在运行的应用,而无需重启应用(在某些情况下,甚至无需重启当前的 Activity)。但是Apply Changes 使用搭载 Android 8.0(API 级别 26)或更高版本的设备上支持。

Apply Changes

在 Android Studio 3.5 及更高版本中,Apply Changes 可让您将代码和资源更改推送给正在运行的应用,而无需重启应用(在某些情况下,甚至无需重启当前的 Activity)。

在这里插入图片描述
Apply Changes 使用搭载 Android 8.0(API 级别 26)或更高版本的设备上支持的 Android JVMTI 实现中的功能。对于UI的改动,我们能通过apply changes快速在手机中查看我们修改的地方。
同时,也存在一定限制:
某些代码和资源更改必须在重启应用之后才能应用,其中包括以下更改:

  • 添加或删除方法或字段
  • 更改方法签名
  • 更改方法或类的修饰符
  • 更改类继承行为
  • 更改枚举中的值
  • 添加或移除资源
  • 更改应用清单
  • 更改原生库(SO 文件)

整体设计需要考虑两个点

  • 1.找出差异是什么
  • 2.如何将修改的代码发送到设备并应用。

我们看下Apply Changes整体框架图

图片名称

比较dex文件

在后台进程中利用D8的DEX文件分析功能来检查Android Studio部署到设备上的每个.dex文件的内容。在.dex文件中的各个类上计算基于校验和的指纹,并将结果临时存储在主机工作站上的缓存数据库中。通过将新编译的指纹与以前的编译的指纹进行比较,“应用更改”能够在短时间内有效地提取更改的类。

增量推送

“应用更改”需要计算安装的APK与最近构建的APK之间不同的文件,而不必从设备中提取所有内容。这次,它仅获取压缩文件的中央目录,并保守估计相应APK之间的差异。通过仅传输已更改的部分,Android Studio传输的数据比完整的APK上传要少得多。在大多数使用情况下,总有效负载减少到几个KiB,而不是几个MiB。

BUCK

BUCK建立了一套完善的依赖规则以及细化的缓存系统来缩减编译时间,其增量构建的原理,实际是以工程目录为单位进行增量构建,发生变更时候,变更的工程,以及该工程作为父节点或祖先节点的工程,均需要重新构建。无论是 Buck 的exopackage,还是代码的增量编译,Buck 都更加高效。Buck还有分包支持,ReDex 支持等功能。

但是BUCK的整体接入成本还是比较高的。要去掉原有的gradle方案

Freeline

多任务并发,多级缓存,增量范围最小化,懒加载,基于长链接无安装式运行期动态替换,基线对齐触发机制,可调试。详细细节可以参考 Freeline的官方文档。

增量编译具体举措

  • 更换编译机器。装备升级,CPU升级效果杠杠的。

  • 升级 Gradle 和 SDK Build Tools。在新版本中,Android致力于优化Gradle的构建。

  • 采用上述增量编译方案的某一种。

代码优化工具

对于debug测试包,我们关心apk构建的速度以便我们能快速开发。而对于需要发布的release包,我们更关心包的大小以及性能的稳定。

ProGuard

接入proGuard后,我们的app构建流程将变化如下。

在这里插入图片描述

d8

d8 是一种命令行工具,Android Studio 和 Android Gradle 插件使用该工具来将项目的 Java 字节码编译为在 Android 设备上运行的 DEX 字节码,该工具支持您在应用的代码中使用 Java 8 语言功能。

在这里插入图片描述

R8

当您使用 Android Gradle 插件 3.4.0 或更高版本构建项目时,该插件不再使用 ProGuard 执行编译时代码优化,而是与 R8 编译器协同工作。
在编译时做了以下事情

  1. 代码缩减 :从应用及其库依赖项中检测并安全地移除不使用的类、字段、方法和属性
  2. 资源缩减:从封装应用中移除不使用的资源,包括应用库依赖项中不使用的资源。
  3. 混淆处理:缩短类和成员的名称,从而减小 DEX 文件的大小。
  4. 优化:检查并重写代码,以进一步减小应用的 DEX 文件的大小。例如,如果 R8 检测到从未采用过给定 if/else 语句的 else {} 分支,则会移除 else {} 分支的代码。(当然有规范的话,我们在日常业务中应该用Lint检测来优化我们的代码,也能方便我们阅读代码)

在这里插入图片描述

R8 的最终目的 加快编译速度,更强大的代码优化。

ReDex

ReDex是Facebook开源的Android字节码(dex)优化器,它直接输入的对象是 Dex,而不是“.class”文件。ReDex拥有目前更强大的编译速度以及代码优化。具体配置可以参考 Redex官方。能有效的降低包提及,优化了android原有的打包策略。

总结

对于如何提高编译速度, google在每个版本升级中都进行了优化,同时,行业内也开源了自身的好的编译方案,对于我们日常的业务开发中,可以根据自身的业务需求来选择方案。
对于我们日常的debug开发,为了能快速看到我们相关功能的具体实现。增量编译的速度成为了关键,在编译过程中,我们看到了一些代码插桩的技术。将在之后给各位详细介绍Android中如何进行代码插桩的方案和一些用例。

参考资料

https://time.geekbang.org/column/article/82468

https://source.android.com/devices/tech/dalvik/dalvik-bytecode

https://developer.android.com/studio/build?hl=zh-cn

https://developer.android.com/studio/command-line/aapt2?hl=zh-cn

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_38366777/article/details/108549231

智能推荐

linux安装crypto库_crypto库的安装linux-程序员宅基地

文章浏览阅读2.8k次。pip3 install -i https://pypi.douban.com/simple pycryptodomePyCrypto 已死,请替换为 PyCryptodome_crypto库的安装linux

使用PLSQL Developer中的ODBC导入器将Excel表导入Oracle数据库-程序员宅基地

文章浏览阅读3.9w次,点赞18次,收藏110次。使用PLSQL Developer中的ODBC导入器将Excel表导入Oracle数据库_odbc导入器

实现Activiti动态调整流程(自由跳转、前进、后退、分裂、前加签、后加签等)_activiti6 动态修改流程-程序员宅基地

文章浏览阅读1.9k次。原文链接:http://www.cnblogs.com/bluejoe/p/5115888.html最近对Activiti做了一些深入的研究,对Activiti的流程机制有了些理解,对动态调整流程也有了一些实践方法。现在好好总结一下,一来是对这段时间自己辛苦探索的一个记录,二来也是为后来者指指路~~~如下内容准备采用QA的方式写,很多问题都是当初自己极疑惑的问题,希望能为大家解惑!Q:..._activiti6 动态修改流程

Modbus通信协议格式以及功能码_modelbus协议功能码-程序员宅基地

文章浏览阅读2w次,点赞4次,收藏43次。Modbus通讯数据格式:单个写入多个写入读取Modbus功能码:功能码 名称 作用1 读取线圈状态 取得一组逻辑线圈的当前状态(ON/OFF)2 读取输入状态 取得一组开关输入的当前状态(ON/OFF)3 读取保持寄存器 在一个或多个保持寄存器中取得当前的二进制值4 ..._modelbus协议功能码

Matlab中图文本中的希腊字母和特殊字符_matlab图文本中的希腊字母-程序员宅基地

文章浏览阅读1.6k次,点赞2次,收藏18次。目录包含希腊字母包含上标和注释TeX 标记选项包含 LaTeX 行间数学公式的文本图文本中的希腊字母和特殊字符是可以使用 TeX 标记向图中添加包含希腊字母和特殊字符的文本。此外,还可以使用 TeX 标记添加上标、下标以及修改文本类型和颜色。默认情况下,MATLAB支持一部分 TeX 标记。要使用其他特殊字符,如积分和求和符号,可以改用 LaTeX 标记。此示例说明如何向图文本中插入希腊字母、上标和注释,并解释其他可用的 TeX 选项。包含希腊字母创建一个简单的线图并添加标题._matlab图文本中的希腊字母

arm64以及amd64和龙芯4000下安装pyqt:_generating the c++ source for the qtcore module...-程序员宅基地

文章浏览阅读3.4k次。arm64以及amd64和龙芯4000下安装pyqt:包下载地址:sip安装包地址:https://riverbankcomputing.com/software/sip/downloadqt源码下载 http://download.qt.io/arhive/qt/2.7下安装pyqt5:https://www.cnblogs.com/deeplearning1314/p/10671347.html参考编译链接:linux下安装qt:https://blog.csdn.net/u01452338_generating the c++ source for the qtcore module...error: unable to create th

随便推点

IDEA基本配置及快捷键_j2eecfgfile插件怎么用-程序员宅基地

文章浏览阅读1.8k次,点赞4次,收藏34次。目录IDEA的快捷键IDEA基本设置★★★jdk设置★★★settings界面介绍一、切换主题二、背景设置三、字体设置四、注释设置五、系统设置六、编码设置七、智能提示忽略大小写八、重复代码横线去除九、设置文件代码模板十、修改servlet模板十一、自定义代码颜色十二、设置鼠标滚轮调节字体大小十三、设置鼠标放在方法上或者变量上会弹出注释说明十四、IDEA给选中内容添加双引号""十五、设置自动导包十六、设置代码显示行号及方法分._j2eecfgfile插件怎么用

Android改变menu中的字体颜色_android menuitem文字颜色-程序员宅基地

文章浏览阅读2.2k次。项目中我们经常用到menu菜单栏,系统给出的默认为黑色的,有时候不符合我们项目中的需要,这就需要我们对状态栏给出一些改变。:解决方案一:在style中配置给出menu的样式,通常情况下,这种方案能决绝大部分需求 <style name="MenuTextStyle"> <item name="android:textColor&_android menuitem文字颜色

如何运用IF语句把数据插入到不同的表中?_if函数将表中数据填到另一个表-程序员宅基地

文章浏览阅读829次。itedbtitle =Request("title")(1); itedbname =Request("name")(1); itedbsource =Request("source")(1); itedbtype =Request("select")(1); itedbcct =Request("content")(1); var conn =Server.CreateObject("ADO_if函数将表中数据填到另一个表

Cuda11.2pytorch安装_cuda11.2安装torch-程序员宅基地

文章浏览阅读2.3k次。pip install torch===1.7.1+cu110 torchvision===0.8.2+cu110 torchaudio===0.7.2 -f https://download.pytorch.org/whl/torch_stable.html -i https://pypi.douban.com/simple/_cuda11.2安装torch

iPS细胞建立的多种选择_成纤维细胞为什么用于ips-程序员宅基地

文章浏览阅读1.3k次。推荐阅读:1.ips细胞再生视网膜研究进展2.iPS细胞重编程实验相关实验材料四因子作用那么,是否可以直接应用相关转录因子对体细胞进行重编程而逆转终未分化细胞的发育潜能, 进而表现出ES细胞那样的多潜能性呢?Takahashi和Yamanaka{4}选择24种在小鼠早期胚胎ES细胞或者肿瘤细胞中丰富表达的转录因子, 通过筛选、组合, 发现用Oct4, Sox2, c-Myc和Klf4 4个因子(这4个因子也被称为Yamanaka因子)可以有效地将胎鼠成纤维细胞(mouse embryonic _成纤维细胞为什么用于ips

微信机器人 wechaty 针对个人微信对接chatterbot +django+drf 实现 自动回复 接入自己api接口-程序员宅基地

文章浏览阅读3.8k次。之前发表过chatterbot 的自定义api本人的流程 但是有粉丝或游客私信我 怎样对接自己个人微信 现在统一回复下 自己的心得流程 实现等在写代码之前我先表述下 之前个人微信接口 使用itchat 等 在使用这类的前提是可以 登录网页版微信 网页版微信登录链接 itchat原理就是一种爬虫 获取绘话信息处理并返回自定义信息(跑远了…) 要想获取个人微信中的对话 必须登录 才能自定义一些逻辑啥的前几天自己写了能实现对接chatterbot的api接口1 首先我们要绕过登录验证 能成功登录

推荐文章

热门文章

相关标签