阅读 645

Speedup:专为项目下Library project过多所设计的加速插件

背景

随着app的持续迭代更新,新功能的持续集成等原因。项目中的library project越来越多。

特别是最近一两年来。流行起来了组件化,这更是直接增加了很多的本地library project的数量。导致每次需要打包apk运行时,都会需要对所有module都进行再次编译。严重影响打包速度。

加速插件简介

Speedup是一款专用于对多module环境下进行打包加速的gradle插件。主要依赖的基本原理有两个:

  1. 动态依赖替换
  2. 打包任务特性

插件地址

github.com/JumeiRdGrou…

动态依赖替换

我们先来看看常规多module环境下的打包流程:

从上图可以看到。每次打包的时候。都会经过以下几个步骤:

  1. 对依赖的module进行进行打包编译,生成各自library的aar依赖。
  2. 对aar依赖进行合并。包括classes.dex的合并、资源合并等。
  3. 将合并后的文件、资源进行打包,生成apk。

而如果。我们使用maven依赖的方式进行依赖的话。编译流程就会优化成下图所示的方式:

可以看到。与上面直接使用module依赖的方式相比:使用Maven仓库依赖的方式,由于maven仓库中提供的直接就是已打包好的aar依赖,所以直接干掉了module -> aar打包编译的过程,起到了加速编译的作用!

所以,现在我们就可以得出第一个结论:使用maven依赖比使用module依赖更快速!

所以这个时候。很多人的常规做法就是。想办法将这些module依赖的library,编译打包发布到各自的远程中央仓库中去,这样就能避免每次运行的时候都去进行额外编译,从而节省时间了。

但是这种做法存在几个问题:

  1. 绝大部分library并不稳定。需要经常修改使用,若放到远程仓库,修改起来很麻烦
  2. 远程仓库的version版本号唯一,每次进行修改后都会需要更新升级版本号再发布。多人协作时容易造成混乱且也不容易维护

所以将module打包发布到远程仓库的做法,并不可取!

现在我们来考虑一下,如果将仓库设置在本地会怎样?也就是说使用本地maven仓库进行依赖管理。

我们来列举一下这里使用本地依赖的优点:

  • 本地仓库中version不唯一

    即可以做到同版本覆盖发布,修改代码后直接重新发布即可,不需要提升version版本。这也正是远程仓库所做不到的。可避免频繁升级改动。

  • 本地仓库不与网络打交道

    读取依赖更快速。事实上,远程仓库中所依赖的库,也是先存放到本地仓库中去再进行读取的。

可以看到,使用本地仓库,可以完美的解决频繁发包的问题!因为如果你的library不需要频繁修改的话,你肯定也早就已经把它单独拆出去放远程仓库中去了~

所以我们只要解决依赖不统一的问题。那么这个加速方案就算是彻底打通了!

打包任务特性

打包任务这里特指的assembleXXX任务。这个任务相信大家平时也见过无数次了。

此任务是由android插件所提供的,编译、合并、打包一条龙的总线任务。平时我们点击运行按钮、打包apk时。也是通过的调用此任务进行打包。打包后通过内置的adb命令将apk发送到手机并执行安装。

这个任务有个很重要的特性。这里我称之为按需加载特性

打个比方:假如我们使用的组件化开发模式,项目依赖链如下图所示:

上图所示的依赖链,对组件化不熟的可能会比较懵逼。对组件化有兴趣的可以去这里了解一下组件化的项目结构:聚美组件化实践之路

这里有三个可运行的壳工程。其各自壳工程的依赖链如下:

  1. [app shell 1] -> [component 1] -> [base library]
  2. [app shell 2] -> [component 2] -> [base library]
  3. [Main app] -> [component 1] & [component 2] -> [base library]

按需加载特性是指:在进行打包任务过程中,打包任务只会触发当前自身依赖链中的module进行编译。而不会对别的非自身依赖链中的module进行编译。

所以,当你运行app shell1的时候。这个时候会参与打包编译的就是[app shell 1]、[component 1]、[base library]这三个module. 这就是按需加载特性

原理

基于以上分析,我建立了此Speedup插件。利用gradle脚本,方便的进行本地aar打包发布,并通过对打包任务流程进行监听,动态的将module依赖替换为本地仓库中的maven依赖。

流程图

这里我画了个简单的流程图。便于直观的理解此gradle插件的运行机制。

特性

  • 原理简单:整个插件由三个文件组成,并有详细注释,很容易理解
  • 配置简单:简单得出乎你意料!
  • 不同机器配置独享,不冲突:
  • 兼容性好:实现逻辑依赖最简单的api。最大程度降低gradle版本升级时sdk改动造成的影响。

使用方式

依赖配置

在项目根目录下添加依赖并添加应用插件

buildscript {
    repositories {
        // 添加jitpack仓库地址
        maven { url "https://jitpack.io" }
    }
    dependencies {
        // 添加插件依赖
        classpath "com.github.yjfnypeu:Speedup:0.9"
    }
}
// 应用插件
apply plugin: 'speedup'
复制代码

属性配置

Speedup支持配置三种属性,均配置于项目根目录下的local.properties文件中。

  • speedup.enable:

    是否激活speedup插件。默认为false,即不激活。这样做的目的是为了使得在发版机器上进行打包apk时。自动忽略speedup插件。进行全量编译

  • localRepo:

    本地maven仓库的地址。默认为项目根目录下的_repo文件夹。若你需要使用别的地址。在此配置个本地地址即可

  • excludeModules:

    由于我们开发的业务线的不同。很多时候我们是需要对部分的module每次都进行即时编译的:比如说现在正在做的功能。是需要对某个lib进行频繁修改的。所以这个时候。就需要使用此属性。指定此lib不需要进行加速。那么每次运行时。都会对此lib进行编译。

把所有属性全放置于local.properties中,出于以下几点考虑:

  • 每个人所需要使用的配置方案不一样,放于local文件中,可以起到配置独立的作用。
  • 发版apk不使用加速方案。一般打包发版apk的均运行在独立的机器上.

配置示例

github上我提供了一个组件化demo地址, 此demo中有添加此插件配置使用。以下内容建议结合demo进行查看。

Android组件化demo展示

由于本地配置是放置于local.properties文件当中。所以默认依赖下来是没有开启激活此插件的。可以参考下方的配置:放入自身的local.properties中去。

speedup.enable=true
# 当需要排除多个module时,中间用英文的逗号隔开
excludeModules=:usercenter:componentusercenter
复制代码

当配置speedup.enable为true,并且同步完成之后。你就可以在gradle任务列表中发现一组speedup任务:

可以看到。speedup任务组中。含有大量的uploadXXX任务提供使用。这些任务都是在同步时根据各个library module自动生成的上传任务。主要目的是将本地的library module方便的进行打包、编译。并发布到本地的仓库中去。提供使用。

当你使用upload进行本地打包发布后,你就可以在项目根目录下的_repo文件夹下找到对应的打包出来的aar:

然后,你可以试试直接运行。并且观察右下角的Gradle Console控制台输出日志。注意看所有执行的任务列表。就会发现:执行的gradle任务明显减少了,并且被打包到本地中的module。并没有被再次进行编译。

这是因为已经在运行时。动态的将对应的module替换为使用本地仓库中的aar地址了。

以上即是此插件的整个用法示例。可以总结为以下几步:

  1. 添加依赖、配置
  2. 执行upload任务进行本地发布
  3. 直接运行。

upload任务列表说明

Speedup插件任务中,很重要的一点即是生成的upload任务列表了。此upload任务主要分为三类:

  1. uploadAll

    作用:用于对项目中所有的library(除了excludeModules所指定排除的module)进行打包发布。此任务默认将会对项目下所有的module执行clean操作。所以可以保证打包出来的aar是不受数据污染的

    使用场景:在进行分支切换后、或者进行合并代码后。此时很多module的数据都有修改。可以使用此任务一次性全部重新打包上传。

  2. uploadForClean

    作用:用于触发项目下所有module的clean任务。将其build文件夹删除。避免打包时出现数据污染。

    与AS自带的clean任务不同。自带的clean任务附带了很多额外的任务,所以每次clean会很慢。而uploadForClean。只会触发所有module自身的clean任务,对自身的build文件夹进行删除,并无其他额外操作。这样clean的速度会得到很大提升。

    此任务一般来说外部不需要直接使用。主要是内部直接提供给uploadAll进行使用

  3. uploadXXX

    作用:用于对指定的module进行重新打包发布。比如uploadbaselib。此任务代表对baselib重新进行打包发布。

评论