当前位置:首页 » 《关注互联网》 » 正文

Android Studio最全面编译构建优化!!!_Nbin_Newby的博客

23 人参与  2022年01月14日 15:41  分类 : 《关注互联网》  评论

点击全文阅读


汇总:Android小白成长之路_知识体系汇总【持续更新中…】

目录

  • 问题背景
  • Gradle构建流程
  • 优化前相关说明
  • 优化方案
    • 初始化速度优化
    • 配置速度优化
    • 执行速度优化
      • 对Gradle进行配置
        • 开启并行编译
        • 增大编译内存
        • 开启按需构建
        • 开启构建缓存
        • 开启增量注解编译
      • 对AS进行配置
        • 开启离线模式
        • 更改AS内存大小
      • 更新最新Gradle版本
      • Module源码转aar
      • 自定义执行的任务
    • Maven代理
    • 使用远程共享构建缓存
  • 总结

问题背景

公司项目使用Android Studio以及Gradle进行编译,在每次修改代码(哪怕是一行修改),再次编译运行都要耗时三四分钟,甚至更长时间。在初次编译时更是长达十几分钟、极大的影响了开发效率。俗话说工欲善其事,必先利其器。这就对编译速度进行一波优化,让我们一步一步开始吧!

Gradle构建流程

首先了解一下Gradle的构建流程,整体分为三个阶段:

  • 初始化阶段:Gradle支持单项目和多项目构建,在初始化阶段,Gradle从setting.gradle中读取需要参与构建的模块,并为每个模块创建一个Project实例。
  • 配置阶段:配置项目模块和其所需要执行的脚本,也就是build.gradle等文件
  • 执行阶段:开始执行配置后的脚本任务

大体上了解了这些流程,我们就可以从这些流程上入手进行优化

优化前相关说明

当前作为验证的电脑相关信息:

  • 电脑名称:MacBook Pro
  • 系统:Mac
  • 内存:8GB 1867MHz DDR3
  • 处理器:双核Intel Core i5 2.7GHz
  • AS版本:4.1.1

验证编译速度的三个角度:

  • rebuild全工程,全部编译
  • 新增一个方法,触发java重新编译
  • 修改一个xml,触发资源重新编译

比较数据获取方式:rebuild尝试三次取最低值,修改方法或xml尝试五次取最低值

相关说明:

  • 由于电脑有时候卡顿或者别的原因影响编译,会使得某次编译耗时很长,因此不能取平均值作为参考
  • 编译一般会一次比一次快,因为Android studio自带缓存
  • 开发阶段本身就不会一直改配置,因此取最小值基本可以模拟日常使用情况
  • 因为主工程模块比较庞大,因此验证时使用的是主工程模块的代码,如果修改的是组件代码,用时一般会更少
  • 每一次数据统计使用的方案继承了它前面所有的优化方案
  • 修改方法和xml用apply changes

优化方案

从整体构建流程可以得知,我们整体上需要从三个方面进行优化:

  • 初始化速度优化
  • 配置速度优化
  • 执行速度优化

其中执行的过程占比是最大的,所以重心放在执行速度优化上

初始化速度优化

一般初始化过程任务较少本身就已经很快了,但仍然可以做一些处理,以达到最佳状态:

  • 当组件化程度较高时,在开发某个特定功能过程中有些组件是不需要引入的,此时可以在setting.gradle中移除不需要引入的组件模块,可以减少初始化时间
  • setting.gradle中include之前尽量不写过多代码

配置速度优化

配置阶段主要是对各个build.gradle进行解析,因此可以注意以下几点:

  • 按需引入模块,减少build.gradle的解析
  • build.gradle中尽量少做耗时操作,例如读取系统时间动态配置apk的名称组成
  • 在开发阶段不是必要执行的任务,可以写判断避免这些任务的配置,例如一些字节码插桩,性能监控之类的

执行速度优化

此阶段存在的大量的任务需要执行,因此优化的点也非常的多

对Gradle进行配置

开启并行编译

开启后会并行执行多个任务,大幅度减少编译时间,只需要在gradle.properties中添加:

org.gradle.parallel=true

增大编译内存

由于大家的电脑配置都不一样,因此具体设置多大内存需要根据个人情况进行合理配置,一般在gradle.properties里已经有相关配置,可以对该配置进行修改,例如

org.gradle.jvmargs=-Xmx4096m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8

同时在主工程模块的build.gradle中进行修改:

dexOptions {
	javaMaxHeapSize "4g"
	}

值得注意的是,javaMaxHeapSize的值需要比org.gradle.jvmargs设置的值少512m以上,而且org.gradle.jvmargs的值并不是设置越高越好,根据验证,最好配置为系统内存的1/3,最多不要超过1/2。在部分文档中显示,高版本中javaMaxHeapSize中不再需要配置javaMaxHeapSize,只需要配置org.gradle.jvmargs即可,查阅了许多资料都没说清楚,所以暂时都配置好了

开启按需构建

对没有更改的模块不再进行编译,非常适合已经组件化的项目,在gradle.properties中添加:

org.gradle.configureondemand=true

开启构建缓存

直接使用之前生成的缓存,不再进行构建,在构建时任务后面会显示FROM CACHE,在gradle.properties中添加:

org.gradle.caching=true

开启增量注解编译

支持注解增量编译,不会重新触发编译(gradle高版本中需要移除),在gradle.properties中添加:

android.enableSeparateAnnotationProcessing=true

数据对比(并行编译是优化前已经开启,因此以下时间不包括并行编译的优化):

rebuild修改方法修改xml
配置优化前4m46s46s22s
配置优化后2m39s42s20s
收益减少44%减少8%减少9%

对AS进行配置

开启离线模式

开启离线模式后不会再开始的时候去检测依赖是否有更新,也不会去下载相关更新的依赖,首次构建不能开启,否则无法完成构建,后续构建可以开启,在某些情况下将大幅度改善编译速度,强烈推荐开发阶段使用。点击下图中的图标的按钮即可开启离线模式,有些版本显示为类似wifi的图标,再次点击取消离线模式:

在这里插入图片描述

更改AS内存大小

点击AS的Help菜单项,选中Change Memory Settings选项。如图:

在这里插入图片描述

弹出如下图弹框,把Maxinum Heap Size 修改为合适值,具体修改值根据自身电脑内存配置选择

在这里插入图片描述

数据对比:

rebuild修改方法修改xml
AS配置修改前2m39s42s20s
AS配置修改后2m16s37s16s
收益减少14%减少11%减少20%

更新最新Gradle版本

由于gradle在新版本中一般都会对构建速度进行进一步的优化,因此保持最新的gradle版本可以获得最佳的构建体验,更新方式如下:

  • 首先在gradle-wrapper.properties中进行gradle版本的配置:

    distributionUrl=https\:``//services.gradle.org/distributions/gradle-6.7.1-all.zip
    
  • 然后在根目录下的build.gradle中更新gradle插件版本:

    classpath 'com.android.tools.build:gradle:4.1.1'
    

更新到6.x以上可能出现的问题和解决方案:

  1. 报异常:

    FAILURE: Build failed with an exception.
    * What went wrong:
    A problem occurred configuring project ':live'.
    > Failed to notify project evaluation listener.
       > org.gradle.api.tasks.TaskInputs.property(Ljava/lang/String;Ljava/lang/Object;)Lorg/gradle/api/tasks/TaskInputs;
       > Could not get unknown property 'additionalParameters' for task ':live:dexBuilderDebug' of type com.android.build.gradle.internal.tasks.DexArchiveBuilderTask.
    * Try:
    Run with --info or --debug option to get more log output. Run with --scan to get full insights.
    * Exception is:
    org.gradle.api.ProjectConfigurationException: A problem occurred configuring project ':live'.
        at org.gradle.configuration.project.LifecycleProjectEvaluator.wrapException(LifecycleProjectEvaluator.java:75)
        ......
        at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56)
    Cause 2: groovy.lang.MissingPropertyException: Could not get unknown property 'additionalParameters' for task ':live:dexBuilderDebug' of type com.android.build.gradle.internal.tasks.DexArchiveBuilderTask.
        at org.gradle.internal.metaobject.AbstractDynamicObject.getMissingProperty(AbstractDynamicObject.java:85)
        ......
        at java.lang.Thread.run(Thread.java:748)
     
    * Get more help at https://help.gradle.org
    

    这是当前greenDao版本过低导致的,更新greenDao版本即可,在根目录的build.gradle下修改版本:

    classpath 'org.greenrobot:greendao-gradle-plugin:3.3.0'
    
  2. 报异常:

    FAILURE: Build failed with an exception.
    * Where:
    Build file '/Users/uxin/AndroidStudioProjects/Pika/UXLiveOverseas/live/build.gradle' line: 253
    * What went wrong:
    A problem occurred configuring project ':live'.
    > Could not get unknown property 'additionalParameters' for task ':live:dexBuilderDebug' of type com.android.build.gradle.internal.tasks.DexArchiveBuilderTask.
    * Try:
    Run with --info or --debug option to get more log output. Run with --scan to get full insights.
    * Exception is:
    org.gradle.api.ProjectConfigurationException: A problem occurred configuring project ':live'.
        at org.gradle.configuration.project.LifecycleProjectEvaluator.wrapException(LifecycleProjectEvaluator.java:75)
        ......
        at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56)
    Caused by: groovy.lang.MissingPropertyException: Could not get unknown property 'additionalParameters' for task ':live:dexBuilderDebug' of type com.android.build.gradle.internal.tasks.DexArchiveBuilderTask.
        at org.gradle.internal.metaobject.AbstractDynamicObject.getMissingProperty(AbstractDynamicObject.java:85)
        ......
        at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56)
     
    * Get more help at https://help.gradle.org
    

    sdk 21之前,一般会使用第三方multidex的依赖开启dex的分块,而sdk 21之後,官方自帶multidex,因此需要去掉第三方的multidex

    • 在主工程模块的build.gradle中,删除掉multidex的依赖和自定义任务:

      //implementation 'androidx.multidex:multidex:2.0.0'
      
      //    afterEvaluate {
      //        tasks.matching {
      //            it.name.startsWith('dex')
      //        }.each { dx ->
      //            if (dx.additionalParameters == null) {
      //                dx.additionalParameters = ['--multi-dex']
      //            } else {
      //                dx.additionalParameters += '--multi-dex'
      //            }
      //        }
      //    }
      
    • 在自定义的Application类中删除multidex的初始化:

      //import androidx.multidex.MultiDex;
      
      //MultiDex.install(this);
      
  3. 报异常:

    FAILURE: Build failed with an exception.
    * Where:
    Build file '/Users/xxx/Projects/xxx/xxx/xxx/build.gradle' line: 1
    * What went wrong:
    A problem occurred evaluating project ':live'.
    > Failed to apply plugin 'com.android.internal.application'.
       > The option 'android.enableSeparateAnnotationProcessing' is deprecated.
         The current default is 'false'.
         It was removed in version 4.0 of the Android Gradle plugin.
         This feature was removed in AGP 4.0
    * Try:
    Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
    * Get more help at https://help.gradle.org
    

    这是上面新添加的注解增量编译字段已经在gradle新版本中被移除了,所以应该去掉,在gradle.properties中删除:

    #android.enableSeparateAnnotationProcessing=true
    
  4. 报异常:

    private static final String LIBRARY_VERSION = ". Version: " + BuildConfig.VERSION_NAME;
                                                                               ^
    符号: 变量 VERSION_NAME
    位置:BuildConfig
    

    由于versionNameversionCode没有多大差别,为了防止概念混淆,官方去掉了VERSION_NAME,因此我们项目中如果仍然需要用到,可与自定义buildConfigVERSION_NAME,在报错的模块的build.gradle中配置:

    defaultConfig {
        minSdkVersion MIN_SDK_VERSION as int
        targetSdkVersion TARGET_SDK_VERSION as int
        versionCode 2
        versionName "1.0.1"
        buildConfigField 'String', 'VERSION_NAME', "\"" + versionName + "\""
    }
    
  5. 报异常:

    
    /Users/xxx/Projects/xxx/xxx/xxx/xxx/src/main/java/com/xxx/base/utils/Utils.java:86: 错误: 找不到符号
          intent.putExtra(PAKAGENAME, BuildConfig.APPLICATION_ID);
                                                               ^
      符号: 变量 APPLICATION_ID
      位置:BuildConfig
    

    Gradle高版本中为了防止library里使用BuildConfig.APPLICATION_ID导致id和appication本身的id理解混淆,需要改为新的字段:

    BuildConfig.LIBRARY_PACKAGE_NAME
    
  6. 报异常:

    
    /Users/xxx/Projects/xxx/xxx/xxx/xxx/src/main/java/com/xxx/base/view/ShareScreenShotDialog.java:205: 错误: 找不到符号
                                            shareInfo.setWeiboCopyWriter(String.format(mContext.getString(R.string.novel_share_intro_wb_empty),
                                                                                                                  ^
      符号: 变量 novel_share_intro_wb_empty
      位置: 类 string
    

    Gradle高版本不允许语言配置中默认语言配置为空,所以需要在default string中添加上报错的那部分string

  7. 报异常:

    Execution failed for task ':live:transformClassesWithAjxForRelease'.
    > Cannot cast object 'com.android.build.gradle.internal.pipeline.TransformTask$2$1@6fe77eee' with class 'com.android.build.gradle.internal.pipeline.TransformTask$2$1' to class 'com.android.build.gradle.internal.pipeline.TransformTask'
    

    这是由于aspectjx版本过低导致,更新版本即可,在根目录的build.gradle中修改:

    classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.10'
    

数据对比:

rebuild修改方法修改xml
Gradle更新前2m16s37s16s
Gradle更新后1m15s33s12s
收益减少44%减少10%减少25%

Module源码转aar

随着业务量的增大,module的引入也会增多,每个module在编译的时候都需要花费一定的时间,即使新版本gradle对未修改并具有缓存的module不进行编译,但取缓存也需要一定时间。把module转化成aar后就不再需要每次都进行编译或者取缓存,可以减少一部分时间

Module转aar优化步骤如下:

  1. 对每个module进行Build->Make module xxx生成aar在build/output/aar

  2. 新建一个module,随意取名字,把其他module生成的aar复制到新建module的libs下,同时把其他module的libs下的aar包也同样复制到新建module的libs下(因为aar包不会包含自己依赖的其他aar包),也可以不新建module,直接放到主工程的libs下

  3. 把新建module的src下其他文件和文件夹删除,只留下main文件夹和AndroidManifest.xml文件

  4. AndroidManifest.xm里的application删除

  5. 在新建module的build.gradle中把其他module所引用的aar依赖全部复制过来,同时依赖上其他几个module制作的aar

  6. 在根目录的build.gradle中修改:

    flatDir {
            dirs 'libs',project(':aar的module').file('libs')
    }
    

用一个新的module来存放aar的好处:

  • 和主工程模块隔开,不需要把aar都复制到主工程的libs,也不需要把依赖写在主工程模块的依赖中
  • 可以在setting.gradle中直接判断选择aar编译还是源码编译
  • 某个组件更新了可以编译一下直接替换aar文件,即时更新

扩展:

可以做一个全局变量控制使用组件源码或者aar,操作步骤如下:

  1. setting.gradle中新增全局开关

    #是否修改组件代码
    isModifyInComponent=false
    
  2. 在主工程模块的build.gradle中修改:

    def modifyInComponent = isModifyInComponent.toBoolean()
    flatDir {
        if (modifyInComponent) {
            dirs 'libs',
                    project(':源码的module').file('libs'),
        }else {
            dirs 'libs',project(':aar的module').file('libs')
        }
    }
     
    if (modifyInComponent) {
        implementation project('源码的module')
    } else {
        implementation project('aar的module')
    }
    
  3. setting.gradle中修改:

    include ':app
    if (isModifyInComponent.toBoolean()) {
        include ':源码的module'
    } else {
        include ':aar的module'
    }
    

最好的方式是搭建私服maven仓库用来存放aar,直接依赖就完成了,更加方便而且容易管理,这里后续再写相应的文章

数据对比:

rebuild修改方法修改xml
组件源码1m15s33s12s
组件aar50s28s11s
收益减少33%减少15%减少8%

自定义执行的任务

在构建过程中,有部分task是为了优化app而执行的。这些task在开发过程中并不需要执行,只需要在正式打包的时候执行即可。因此可以暂时关闭这些任务,以减少执行时间。可以引入一个全局变量当做开关,然后在这些任务插件引入的地方做判断,按需执行。

操作步骤如下:

  1. gradle.properties中定义一个开关

    #开启快速编译模式,快速编译舍弃了一些配置,可以较快编译执行app,适合开发调试阶段
    isFastBuildMode=false
    
  2. 在主工程模块的build.gradle中做判断,例如下面的:

    def fastBuildMode = isFastBuildMode.toBoolean()
    if (fastBuildMode) {
        repositories {
            flatDir {
                    dirs 'libs',
                            project(':aar的module').file('libs')
            }
        }
    } else {
        apply plugin: 'org.greenrobot.greendao'
        apply plugin: 'walle'
        apply plugin: 'com.didiglobal.booster'
        apply plugin: 'android-aspectjx'
        repositories {
            flatDir {
                    dirs 'libs',
                            project(':源码的module').file('libs')
            }
        }
        greendao {
            schemaVersion 2
            targetGenDir 'src/main/java'
        }
        walle {
           ...
        }
     
        aspectjx {
            exclude 'com.alipay', 'com.tencent', 'com.squareup.leakcanary'
        }
    }
     
    if (fastBuildMode) {
        ndk {
            abiFilters 'armeabi-v7a'
        }
        resConfigs "cn", "xhdpi"
     
    } else {
        ndk {
            abiFilters 'armeabi-v7a', 'arm64-v8a'
        }
    }
    

注意:开启快速编译开关会关闭一些任务,所以可能会导致一些不可预知的问题,如果调试过程中出现异常,可以关闭快速编译开关重新尝试,在打包apk发布的时候一定要记得关闭快速编译开关

数据对比:

rebuild修改方法修改xml
关闭快速编译50s28s11s
开启快速编译38s13s11s
收益减少24%减少53%持平

Maven代理

前面说过可以创建私服maven用来存放生成的aar,其实也可以用私服maven来代理需要下载的依赖,放在内部网络仓库中,需要的时候直接从内网中读取,而无需去远程maven仓库读取,这对一些使用外网的maven仓库的项目具有非常大的帮助,如果不想自己搭建,也可以使用阿里云的镜像maven仓库,里面有常用的一些仓库镜像,搭建方法后续更新一篇文章来说明

使用远程共享构建缓存

前面提到过开启缓存的方式,但是那只针对于本地缓存,在首次编译时,缓存为空,仍需要大量的时间进行编译。但是在公司开发过程中,通常已经有同事的机器或者CI构建已经进行了编译构建,我们能不能用某种方式来使用他们的缓存呢?这样就解决了首次编译时间过长的困境,办法总比困难多,对共享构建缓存感兴趣的可以去查看这篇文章:Gradle使用远程构建缓存

总结

比较安全简单优化方案:

  • 开启并行编译、按需构建、构建缓存
  • 开启注解增量编译(Gradle插件4.0以下)
  • 开启离线模式
  • 使用apply changes
  • 修改JVM大小

需要适配的优化方案:

  • 升级gradle及其插件
  • 使用官方muitidex

只适合调试开发阶段的极速方案:

  • module转aar
  • 按需自定义执行任务

更深入的优化:

  • 自定义编写优化插件,提高缓存命中率等

  • 私服maven镜像代理

  • CI共享构建缓存

Mac系统总收益:

rebuild修改方法修改xml
优化前4m46s46s22s
优化后38s13s11s
收益减少86%减少46%减少50%

Windows 系统验证

配置:

  • 处理器:i7-10510U 2.3GHz
  • 内存:8GB
  • 固态硬盘

总收益:

rebuild修改方法修改xml
优化前10m30s2m33s12s
优化后1m9s12s9s
收益减少89%减少92%减少25%

数据可能不完全准确,但编译速度是肉眼可见地飞升,优化后可以大幅度减少编译时间,这时间拿去喝咖啡它不香嘛,如果经费充足,再更新一波电脑配置,速度直接起飞,少加班就靠这个优化了!


点击全文阅读


本文链接:http://zhangshiyu.com/post/33256.html

编译  优化  修改  
<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1