文档章节

Gradle模块化配置:让你的gradle代码控制在100行以内

SuShine
 SuShine
发布于 2018/10/09 20:19
字数 2540
阅读 21
收藏 0

概述

我们知道,Android Studio是利用gradle进行构建的,我们经常接触到的gradle脚本是build.gradle,build.gradle有两个,一个在project下,一个是在app目录下,随着项目的迭代,我们会在app目录下的gradle中添加很多依赖,project下的gradle却不会发生很大的变化,所以会导致app下面的gradle文件越来越大,有时候查找对应的方法以及task非常不方便,尤其是在集成了tinker热修复之后,app下面的gradle已经达到了将近1000多行,最近刚好有时间认真研究了一下gradle,确切地说是groovy,然后通过脚本依赖实现了gradle解耦,成功的把app目录下的gradle代码控制在100行以内。

正文

常见配置

通常的做法是在project目录下新建一个config.gradle文件,如下:

ext {
    android = [
            compileSdkVersion: 25,
            buildToolsVersion: "25.0.3
    ]
    supportLibrary = "25.4.0"
    tinkerVerison = "1.9.1"
    dependencies = [
  "multidex"       : "com.android.support:multidex:1.0.1",
  "okhttp3"       : "com.squareup.okhttp3:okhttp:3.9.0"
}

然后在project下的build.gradle文件中引用

apply from: "config.gradle"

再接着在app目录下的build.gradle中获取并使用

apply plugin: 'com.android.application'
apply from: "package.gradle"

def cfg = rootProject.ext.android
def librarys = rootProject.ext.dependencies

android {
    compileSdkVersion cfg.compileSdkVersion
    buildToolsVersion cfg.buildToolsVersion
    dexOptions {
        jumboMode = true
    }
//此处省略一万行代码
}
dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    compile librarys["multidex"]
    compile librarys["okhttp3"]
//此处省略一万行代码
}

这种方式能够将我们的gradle统一进行管理,虽然并不能减少app的目录下的build.gradle的代码量之前,但是觉得够用了,本身对基于groovy的gradle不是很熟,虽然随着项目迭代,app目录下的build.gradle代码量越来越大,尤其是当项目集成了tinker之后,而后集成了packer-ng-plugin打包,以及加入了一些自定义的Task之后,代码会显得非常臃肿,有时候改一个东西,需要找很久,由于对groovy不是很熟,在网上也看过一些文章,基本上都是在介绍gradle的基本知识以及依赖统一管理,加上在gradle里面写代码没有提示,一度让我以为这可能就是build.gradle的最终版了,直到最近项目刚上线,稍微有点空闲,然后决定彻底简化一下gradle代码。

url优化

按照config的配置,一般来讲,我们开发的时候至少会有两个服务器地址,正式跟测试,为了统一管理,先是写死在buildType里面的

 buildTypes {
        release {
            minifyEnabled true
            shrinkResources true
            buildConfigField "String", "AlphaUrl", "\"releaseUrl1\""
//          buildConfigField "String", "AlphaUrl", "\"releaseUrl2\""
        }
        debug {
            minifyEnabled true
              buildConfigField "String", "AlphaUrl", "\"debugUrl1\""
//            buildConfigField "String", "AlphaUrl", "\"debugUrl2/\""

        }
    }

当我们的服务器地址只有一个正式的或者一个测试的时候,这样写完全OK的,但是如果是有多个地址的话,你每切换一次,都需要重新同步一下,还有就是,多人协作开发的时候,每次从Git服务器上面更新代码,只要更新到app目录下的gradle,都是需要重新同步的,参照config的配置,我们如果是引用的话就每次只需要读取引用的那个url,切换只需要修改config中的代码就可以了,下面是进行的优化:
在config中添加代码

    url = [
  "debug"  : "debugUrl1",
//"debug"  : "debugUrl2",
//"debug"  : "debugUrl3",
   "release": "releaseUrl1",
// "release": "releaseUrl2"

    ]

重新引用

//获取
def url = rootProject.ext.url
//使用
//debug
buildConfigField "String", "AlphaUrl", "\"${url["debug"]}\""
//release
buildConfigField "String", "AlphaUrl", "\"${url["release"]}\""

依赖优化

先看一看之前的代码

dependencies {
 compile fileTree(include: ['*.jar'], dir: 'libs')
 compile librarys["multidex"]
 compile librarys["supportAppcompat"]
 //此处省略一万行代码

实际上library就是一个Map,其实这句代码转化成Java就是

 compile fileTree(include: ['*.jar'], dir: 'libs')
 HashMap<String,String> hashMap=new HashMap<>();
 compile hashMap.get("multidex")
 compile hashMap.get("supportAppcompat")

其实可能你也知道了,实际上我们完全可以写一个循环来简化这些代码,就跟HashMap的遍历一样

    dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    librarys.each { k, v -> compile v }
}

模块化配置

在我们进行常规的gradle配置中,我们并没有在project下的build目录添加很多代码,只是新建了一个config.gradle文件,然后再project目录下添加了一行依赖,所以就能够调用config中的代码,同样的,我们也可以把tinker,packer-ng-plugin,以及自定义task的配置文件用一个gradle文件进行配置,然后在app的目录下进行引用,实际上就是利用了gradle的插件依赖。

tinker配置

首先在app的目录下新建一个tinker.grale配置文件,为什么是tinker目录下,因为tinker的运行需要依赖'com.android.application'这个插件,所以必须放在这个目录下,然后复制粘贴tinker的配置代码,注意不要忘记修改tinker的id,我是在project目录下统一进行配置的tinker,所以只需要调用config中的代码

//此处省略一万行代码

def getTinkerIdValue() {
    return rootProject.ext.tinker.id

}
//此处省略一万行代码

然后在app下面的build.gradle中进行引用,注意看一下这行代码的位置,不要放在最开始,因为在依赖tinker.gradle文件的时候,不然你是打不了tinker的补丁的,tinker需要读取application的一些信息,放在buildTypes 之后,当时我也是调试了好久。

apply plugin: 'com.android.application'
def cfg = rootProject.ext.android
def librarys = rootProject.ext.Dependencies
def tinker = rootProject.ext.tinker
def url = rootProject.ext.url

 buildTypes {
  //此处省略一万行代码
}
apply from: "tinker.gradle"

到此,tinker就可以使用了,下面继续配置packer的gradle

packer配置

新建package.gradle文件

apply plugin: 'packer'
packer {
    archiveNameFormat = '${buildType}-v${versionName}-${channel}'
    archiveOutput = new File(project.rootProject.buildDir, "apks")
    channelList = ['xiaomi','meizu']
}
}

在app下的build.gradle中添加依赖

apply plugin: 'com.android.application'
apply from: "package.gradle"

task配置

虽然Android Studio的application自带了很多task,但是并不能满足我们有些需求,比如我需要用Python将测试包上传至fir,就需要自定义task,所以我也打算把这部分给分离出来,新建upload.gradle

ext {
   //此处省略一万行代码
    startUpload = this.&startUpload
}
//上传至fir
def startUpload() {
 //此处省略一万行代码
}

在project中引用

apply from: "upload.gradle"

在app的build.gradle中可以直接调用

task toFir << {
    startUpload()
}

toFir <<,其实是gradle的语法,如果不加<<的话,每次编译的时候都会执行这个task,加了<<,只有执行这个task的时候才会执行里面的代码

gradle代码.png

只有81行,这样一来,调试就很轻松了,哪个脚本除了问题,就直接去调试相应的脚本就好了,不用在自己的gradle里面改来改去。

运行测试

tinker 测试

运行命令gradlew tinkerpatchDebug或者打开右侧的可视化工具栏点击tinker下的tinkerpatchDebug,运行测试,运行结果:

Result: final signed patch result: G:\Note\ChuangMei\app\build\outputs\tinkerPatch\debug\patch_signed.apk, size=2110
Result: final signed with 7zip patch result: G:\Note\ChuangMei\app\build\outputs\tinkerPatch\debug\patch_signed_7zip.apk, size=2439
Warning: patch_signed_7zip.apk is bigger than patch_signed.apk 329 byte, you should choose patch_signed.apk at these time!
Tinker patch done, total time cost: 8.806000s
Tinker patch done, you can go to file to find the output G:\Note\ChuangMei\app\build\outputs/tinkerPatch/debug
-----------------------Tinker patch end-------------------------
BUILD SUCCESSFUL in 3m 16s

packer测试

运行命令gradlew clean apkDebug运行测试,运行结果:

> Task :app:apkDebug
============================================================
PackerNg - https://github.com/mcxiaoke/packer-ng-plugin
============================================================
Variant: debug
Input: G:\Note\ChuangMei\app\build\outputs\apk\app-debug.apk
Output: G:\Note\ChuangMei\build\apks
Channels: [xiaomi meizu]
Generating: debug-v1.6.3-xiaomi.apk
Generating: debug-v1.6.3-meizu.apk
Outputs: G:\Note\ChuangMei\build\apks

task测试

运行命令 gradlew task toFir,运行结果:

开始上传至fir
http://api.fir.im/apps
success_apk:{"is_completed":true}
success_icon:{"is_completed":true}
上传结束 with value 0

几点说明

gradle所放位置

为什么有的是放在project目录下,有的是放在app的目录下,因为gradle引用默认的是当前路径,这样放我引用的时候就不需要去配置所引用的gradle路径,当然如果你原因放置在同一个目录下面也是OK的,只需要在apply的时候加上引用的path即可。

引用位置

为什么有的引用是放在头部,有的引用需要放置在中间,这个取决于引用的插件是否需要读取application的配置信息,如果是tinker,必须放置在中间,因为它生成patch包需要获取很多application信息,如果是packer打包的话,则不需要,这个需要格外留意一下,不然会有很多莫名其妙的错误。

方法调用

在同一个gradle脚本里面,方法调用是很简单的,但是当我们有多个gradle脚本的时候,如何相互调用彼此的方法呢,其实我之前想优化的时候,也是卡在这里,因为属性调用很简单,gradle提供了ext,所以我们可以很容易的获取其他gradle的属性,如果我们现在有两个gradle,一个是first.gradle,一个是second.gradle,我想在second.gradle里面调用first.gradle中的方法,应该怎么做呢?
只需要在first.gradle中进行如下配置

ext{ 
    test= this.&test
 } 
    def  test(){  
       println("我被调用了")
}  

然后在second.gradle中进行配置

//直接调用
   test()
//通过task调用
task CustomTask << {
    test()
}

关于代码

package
需要配置签名,在keystore.properties中进行配置,可以进行多渠道打包
tinker
为了保证对gradle进行模块化分离不影响项目的构建,所以我都是用自己的真实项目进行构建的,因为简单的Demo很难模拟真实项目中的构建环境,所以demo中我只提供了gradle的配置文件,没有进行tinker的配置,但是已经对tinker进行了模块化分离。
upload
上传至fir的代码是利用Python脚本进行上传的,需要配置Python环境以及安装requests库,感兴趣的话可以查看一下我之前的文章Python(一)Android借助Python实现自动打包上传fir

代码下载



作者:wustor
链接:https://www.jianshu.com/p/8d52afc1057d
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

本文转载自:https://www.jianshu.com/p/8d52afc1057d

共有 人打赏支持
SuShine
粉丝 126
博文 569
码字总数 154706
作品 0
朝阳
后端工程师
私信 提问
读书笔记--Android Gradle权威指南(上)

最近看了一本书《Android Gradle 权威指南》,对于 Gradle 理解又更深了,但不想过段时间就又忘光了,所以打算写一篇读书笔记,将书中一些我个人觉得蛮有用的点记录、总结一下。 前言 首先,...

请叫我大苏
2018/05/24
0
0
Gradle之介绍

Gradle是基于JVM构建工具的新一代版本。它从现有的构建工具如Ant和Maven中学到了很多东西,并且把它们的最优思想提升到更高层次。遵循基于约定的构建方式,Gradle可以用一种声明式的方式为你...

柳哥
2015/10/10
69
0
AndroidStudio与Gradle插件和Gradle

一.概况 由于公司的一个项目用的Gradle插件和Gradle版本比我电脑上用的高,所以工程加载到本地之后就开始各种报错了。在升级gradle插件和gradle的过程中遇到了很多的坑。所以为了以后更好的解...

天王盖地虎626
01/12
0
0
使用gradle构造Java工程

大家学习一个新工具,第一步一般是在自己的机器上手动安装其。 打开工作空间,新建目录结构如下: └── src 之所以要这样,是因为gradle的Java插件默认扫描这样的目录来查找Java代码。 he...

davelet
2016/03/07
0
0
Android Studio导入Project、Module的正确方法

Gradle Project项目、Module模块导入 最近看到网上很多人在抱怨,Android Studio很难导入github上下载下来的一些项目,主要包括: 1、导入就在下载Gradle 2、根本导不进 下面我分2部分来讲解...

eclipse_xu
2014/11/15
0
0

没有更多内容

加载失败,请刷新页面

加载更多

什么是JSONP?

json底层原理: 1.他是利用<script/>里的“src”标签能 进行跨域请求的特性加载资源,但是他加载到的资源会当作一个js脚本解析,所以我们得给他返回一个js脚本。 2.所以我们用一个方法名“f...

红土豆
36分钟前
3
0
ByteBuffer详解

在Java nio中,主要有三大组件:Buffer,Channel和Selector。这三者之间的关系可以按照如下方式进行理解: Buffer提供了一个字节缓冲区,其可以不断的从Channel中读取接收到的数据。Buffer的...

爱宝贝丶
37分钟前
5
0
Maven【私有仓库、上传jar包、引用私服jar包、上传本地项目到私服】

搭建私有服务器 前面已经说过了,我们使用Maven的使用,如果需要导入相对应的jar包,Maven首先会在我们的本地仓库中寻找—>私有仓库—>中心仓库… 然而,我们的本地仓库常常没有想要的jar包的...

Nonry
53分钟前
3
0
VARCHART XGantt实践:兼顾清晰和细节的排列优化

VARCHART XGantt是一款功能强大的甘特图控件,其模块化的设计让您可以创建满足需要的应用程序。XGantt可用于.NET,ActiveX和ASP.NET应用程序,可以快速、简单地集成到您的应用程序中,帮助您...

ymy_666666
54分钟前
3
0
Syncfusion教程:在Xamarin.Forms中创建数据输入表单 (1)

下载Essential Studio for Xamarin最新版本 Essential Studio for Xamarin是全面的Xamarin.iOS、Xamarin.Android和Xamarin.Forms组件套包,包含最快的图表和网格。 介绍 欢迎学习使用Syncfus...

电池盒
54分钟前
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部