关于android:Android-编译优化

31次阅读

共计 4244 个字符,预计需要花费 11 分钟才能阅读完成。

软件研发中,消耗最多的并不是编写代码,而是代码编译和代码一直调试的过程。对于咱们 Android 来说,随着我的项目的一直迭代,以及业务模块的一直减少,我的项目技术栈的减少,我的项目编译会越来越慢。随着业务的扩大,置信很多的公司都曾经做了模块化 / 组件化。

背景

创立一个 Project 后能够创立多个 Module,这个 Module 就是所谓的模块。一个简略的例子,可能在写代码的时候咱们会把首页、音讯、我的模块拆开,每个 tab 所蕴含的内容就是一个模块,这样能够缩小 module 的代码量,然而每个模块之间的必定是有页面的跳转,数据传递等,比方 A 模块须要 B 模块的数据,于是咱们会在 A 模块的 gradle 文件内通过 implementation project(':B')依赖 B 模块,然而 B 模块又须要跳转到 A 模块的某个页面,于是 B 模块又依赖了 A 模块。这样的开发模式仍然没有解耦,改一个 bug 仍然会改变很多模块,并不能解决大型项目的问题。于是就有了组件的概念,咱们日常业务需要开发的组件叫做业务组件,如果这个业务需要是能够被广泛复用的,那么叫做业务根底组件,譬如图片加载、网络申请等框架组件咱们称为根底组件。于是一个典型的组件化架构通常如下图所示。

实线示意间接依赖关系,虚线示意间接依赖。比方壳工程必定是要依赖业务根底组件、业务组件、module_common 公共库的。业务组件依赖业务根底组件,但并不是间接依赖,而是通过”下沉接口“来实现间接调用。业务组件之间的依赖也是间接依赖。最初 common 组件依赖所有须要的根底组件,common 也属于根底组件,它只是对立了根底组件的版本,同时也提供了给利用提供一些形象基类,比方 BaseActivity、BaseFragment,根底组件初始化等。

编译优化

Android 编译流程

Android apk 的编译构建分为四个步骤:

  1. 代码编译:将源代码,R 文件,AIDL 生成的文件等 编译成.class 文件;
  2. 代码合成:通过 dex 工具将.class 文件和工程依赖的第三方库文件生成虚拟机可执行的.dex 文件,如果应用了 MultiDex 会产生多个 dex 文件;
  3. 资源打包:apkbuilder 工具将.dex 文件,apt 编译后的资源文件,三方库中的资源文件打包生成签名对齐的 apk 文件;
  4. 签名和对齐:应用 Jarsigner 和 Zipalign 对文件进行签名和对齐,生成最终的 apk 文件。

以下是 gradle 编译一个 app module 的 task 链:

gradle clean assembleDebug -x lint check –stacktrace

:app:clean  // 清理上次编译的遗留,删除 module 下的 build 文件夹
:app:preDebugBuild  //debug 版本预编译
:app:checkDebugManifest //AndroidManifest 查看
:app:prepareDebugDependencies   // 查看 debug 版本的依赖
:app:compileDebugAidl   // 编译 debug 版本的 aidl 文件
:app:compileDebugRenderscript   // 编译 Renderscript 文件
:app:generateDebugBuildConfig   //generated/source 文件夹下,生成 buildConfig 文件夹
:app:generateDebugAssets    // 生成 Assets 文件到 generated 下的 asset 文件夹
:app:mergeDebugAssets   // 在 intermediates 下生成 assets 文件夹,将其余 module/aar 中的 assets 文件拷贝过去
:app:generateDebugResValues // 生成 res value 文件
:app:generateDebugResources // 生成 Resources 文件
:app:mergeDebugResources    //merge(合并)资源文件
:app:processDebugManifest   // 将 merge 后的 Manifest 文件放在 intermediates/manifests 文件夹下
:app:processDebugResources  // 解决资源文件,生成 R.txt 文件,同时也生成对应的 multidex 文件夹
:app:generateDebugSources   // 合成资源文件在 generated 文件夹下生成对应的 R.java 文件
:app:compileDebugJavaWithJavac  // 应用 javac 生成 java 文件
:app:compileDebugNdk    //ndk 编译
:app:compileDebugSources    // 编译资源文件
:app:transformClassesWithDexForDebug    // 将.class 文件转换成.dex 文件
:app:mergeDebugJniLibFolders    // 合并 jni(.so)文件
:app:transformNative_libsWithMergeJniLibsForDebug   // 转换 jni 文件
:app:processDebugJavaRes    // 解决 java 资源
:app:transformResourcesWithMergeJavaResForDebug // 转换 java 资源文件
:app:validateSigningDebug    // 验证签名
:app:packageDebug   // 打包
:app:assembleDebug  //apk 编译实现

开启 InstantRun

Android Studio 2.0 推出了 InstantRun,意为霎时编译,在编译开发时缩小利用的部署及构建工夫。如果须要开启 InstantRun,须要 Gradle2.0 和 minSdkVersion15 以上版本。

构建流程 :代码变更 –> 编译 –> 利用构建 –> 利用部署 –>app 重启 –>activity 重启 –> 实现批改变更
实现即时运行的机制:批改代码后,增量构建(产生增量 dex),而后通过判断更新资源的复杂度去抉择执行热更新,温更新或者冷更新;

  • 热部署:失效时不须要重启 app,也不须要重启 activity
  • 温部署:重启 activity 后能力看到更新
  • 冷部署:app 须要重启,但不是重新安装

InstantRun 次要干了两件事:

  1. 应用 manifest-merger 整合我的项目的 manifest,通过 aapt 工具将合成的 AndroidManifest.xml 文件与 res 资源编译到增量 apk 中;
  2. 代码批改后,通过 javac 将 java 文件编译成 class 文件,而后打包成 dex 文件,同样搁置在增量 apk 中;

gradle 编译优化

咱们晓得,Android 工程是应用 gradle 进行构建的,所以,优化 Android 的编译工夫,在 gradle 方面有很多的措施。

properties 配置优化

# 开启并行编译,仅仅实用于模块化我的项目(存在多个 Library 库工程依赖主工程)org.gradle.parallel=true          
# 应用编译缓存
android.emableBuildCache=true
# 开启构建缓存,Gradle 3.5 新的缓存机制,能够缓存所有工作的输入,#  不同于 buildCache 仅仅缓存 dex 的内部 libs,它能够复用任何时候的构建缓存,设置包含其它分支的构建缓存    
org.gradle.caching=true
# 构建初始化须要执行许多工作,例如 java 虚拟机的启动,加载虚拟机环境,加载 class 文件等等,# 配置此项能够开启线程守护,并且仅仅第一次编译时会开启线程(Gradle 3.0 版本当前默认反对)# 保障 jvm 编译命令在守护过程中编译 apk,daemon 能够大大减少加载 jvm 和 classes 的工夫
org.gradle.daemon=true          
# 最大的劣势在于帮忙多 Moudle 的工程提速,在编译多个 Module 相互依赖的我的项目时,# Gradle 会按需抉择进行编译,即仅仅编译相干的 Module    
org.gradle.configureondemand=true           
# 配置编译时的虚拟机大小,加大编译时 AndroidStudio 应用的内存空间
# -Xmx2048m:指定 JVM 最大容许调配的堆内存为 2048MB,它会采纳按需分配的形式。#-XX:MaxPermSize=512m:指定 JVM 最大容许调配的非堆内存为 512MB,同上堆内存一样也是按需分配的。org.gradle.jvmargs=-Xmx3072m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8

过滤 gradle task

在执行构建工作时,选择性的去除并不需要运行的 gradle task 工作。

tasks.whenTaskAdded(new Action<Task>() {
    @Override
    void execute(Task task) {if (task.name.contains("lint")     // 不扫描潜在 bug 能够应用该项
                || task.name == "clean"
                || task.name.contains("Aidl")     // 我的项目中用到 Aidl 则不能够舍弃这个工作
                || task.name.contains("mockableAndroidJar")// 用不到测试时能够先敞开
                || task.name.contains("UnitTest")// 用不到测试时能够先敞开
                || task.name.contains("AndroidTest")// 用不到测试时能够先敞开
                || task.name.contains("Ndk") || task.name.contains("Jni")// 用不到 NDK 和 jni 时敞开
        ) {task.enabled = false}
    }
})

应用本地 gradle

应用本地的 gradle 文件,防止从网络拉取的状况。

其余

将不须要频繁改变的 module 从 setting.gradle 中去掉,间接援用 module 对应的 aar 文件。工程中有多个 module 时,会先编译每一个 module 之后再编译主工程,尽量少的 module 依赖必定会放慢编译速度。另外,如果你应用的是 Kotlin+JetPack 形式来构建的 Android 我的项目,那么能够尝试应用 KSP:辞别 KAPT,应用 KSP 为 Android 编译提速

正文完
 0