浅谈Gradle(二)与SourceSets

之前写过了一篇Gradle(一),那是根据别人写的文章总结写的,当时其实还是很多不懂,包括我现在对gradle的理解也其实还是似懂非懂,但是还是要写,每次写完之后包括再重新多看几次,都会有新的感悟。
然后我想说的是关于Gradle的文章,其实网上写得好的并不是很多,就那一两篇写得比较好,然后其他都是千篇一律,还是比较建议就是看官方的文档,但是官方的文档其实有时候也读不懂他说的到底是个什么意思,所以我这里比较推荐两本书:《Android Gradle权威指南》和《Gradle of Android 中文版》
其实这两本书也并不是把所有的东西都讲得很清楚,但是读之后会至少有个概念。当然如果以前没接触脚本的话估计看一遍还是看不懂,包括我其实看了很多一样的内容,但是现在也还有很多不知道。

一.Gradle

相关的内容比较多,我就不一点一点开始讲,就贴个别人写好的文章出来,但我个人还是比较建议去看书
https://www.cnblogs.com/ut2016-progam/p/5871430.html

1.gradle

gradle是什么呢,我个人简单的理解就是他是一个构建工具,他有个特性是约定优于配置,他是基于Groovy的领域专用语言(DSL),其实这个DSL我也不懂具体是什么意思。
关于Groovy语言,其实我感觉和js还是比较相似的,是不是所有的脚本语言都一个尿性,建议还是要看一下,其他的都还好,主要需要注意一下它的那个闭包的概念,也就是Closure类型。

2.生命周期

比较重要的一点是gradle的构建过程是有生命周期这一概念的,分为三个阶段:
(1)初始化阶段:创建 Project 对象,如果有多个build.gradle,也会创建多个project
(2)配置阶段:在这个阶段,会执行所有的编译脚本,同时还会创建project的所有的task,为后一个阶段做准备
(3)执行阶段:在这个阶段,gradle 会根据传入的参数决定如何执行这些task,真正action的执行代码就在这里

其实可以先把每一个.gradle文件当成一个Project ,初始化阶段就是把.gradle变成Project对象,第二个阶段就是创建Project对象里面的任务,我个人理解就是走.gradle文件里面的代码(但是估计这个解释不是很对,我对第二个阶段不是很清楚),第三个阶段就是执行这些任务。

那么什么时候开始走这个生命周期呢,不知道,我没有找到有哪篇文章说这个的,但是从我打印的结果来看,我认为是只要执行某些任务都会走这3个生命周期,比如说Rebuild或者运行程序,都会执行生命周期,比如这段Rebuild时的打印内容


第一个部分就是执行的任务,其实在AS的工具栏点按钮,相当于在命令行输入要执行的命令,第二个部分是我在gradle文件里面写的一些打印的操作,我觉得这个就是配置阶段,因为你修改了Gradle文件中的内容,它需要重新进行配置吧,第三个部分前面有冒号的就是执行任务,也就是执行阶段。
所以对生命周期我是这样认为的,初始化阶段只要setting.gradle文件没变,它就只进行初始化一次,如果这个文件变了,就需要重新进行初始化。然后是配置阶段就是执行项目Gradle和模块gradle里面的代码,每次执行任务都会重新执行配置阶段吧。然后执行阶段就是打印中的最后一部分,很明显能看出,也是每次操作都会执行这个生命周期。

其实我在开发gradle的时候遇见了一个BUG,点击sync now之后,会先跑一遍日记,然后刷新再跑一遍,在第一遍打印的结尾会打印这个



要不是我手快都截不到图,可以看出这时候是配置阶段,而这时候还没生成variant,所以我当时获取variant出现了为空的BUG。我感觉这个阶段也是多渠道资源进行合并的阶段。

3.插件

在android中Gradle还有一个比较重要的概念就是插件。在根目录下的build.gradle文件,也就是整个工程的build.gradle文件

buildscript {
    
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.1.2'
        

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        google()
        jcenter()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

里面某块表示什么意思,网上也有很多说明,因为我不经常改动这个文件,所以这里我就别在不懂装懂了。

然后就是每个module的gradle文件,这个是比较常用的,一般都会这样写:

apply plugin: 'com.android.application'
android {
        .....
}

这个就是插件,gradle里面引用了com.android.application这个插件。这个是一个闭包,如果之前看过groovy就知道,可以看到Gradle里面基本所有的地方都用到闭包的写法。android这个关键字传入的闭包里面的defaultConfig啊buildTypes啊这些都是属于这个插件的内容,但也不一定,因为这个插件是是扩展了java插件,举个栗子,里面的sourceSets就是java插件的。所以要想知道里边具体是怎么配置的,可以搜索这个插件的内容。常用到的就大概这些
defaultConfig是基本的配置信息
buildTypes和productFlavors我在多渠道打包的文章有详细讲
sourceSets就是源集,等下会详细去讲一点
其他的详细内容也不太想多说什么,因为一篇文章肯定是写不完所有配置里具体该详细怎么做的,我这里暂时先打算说SourceSets

二.SourceSets

SourceSets被称作原集合,一般可以用它来指定资源的路径。



比如我这个地方,我想把两个文件夹中的代码和原文件夹中的进行合并,我可以这样写

 sourceSets{
        main{
            java {
                srcDirs "src/mtone"
                srcDirs "src/mttwo"
            }
        }
    }

可以看到这个set,学过java的都知道这是集合,其实gradle里面很多地方可以用java代码去写
我们打印看看这个set是什么

 sourceSets{
        main{
            java {

                srcDirs "src/mtone"
                srcDirs "src/mttwo"

                sourceSets.all{set ->
                    println "${set.name}的文件是 ${set.java.srcDirs}"
                    }
                }

            }
        }
    }

可以看到打印的结果



set的name其实就是基本和渠道差不多,然后java.srcDirs就是这个set的java文件的路径,这是一个我比较想说的点,前面我说了指定资源路径,java其实是其中一个,我们现在可以来看看大概有什么资源文件




所以你可以写一个文件夹,然后在文件夹写一些资源文件,当你某个渠道想要把这些资源文件加进去的时候,你可以这样写
 渠道名{
        main{
            res{
                 srcDirs "你自己资源文件夹的路径"
            }
        }
    }

这样就可以在编译时把你自己的资源文件和原项目的进行合并,当然会有一定的合并规则,这个我不多说,你可以百度“资源合并规则”。还有一点是我觉得并不是在编译时才合并的,而是在配置的生命周期时执行sourceSets里面的操作,对所有的资源进行合并。

然后我再说一个场景

我这三个包都要进行合并的,但是出现了这样的一个问题,我想很多人都碰过这个问题,没错,就是重复类的问题,这时我这里的main也就是原本的目录下有个MyTestOne.java类,但是我的mtone目录下也有一个MyTestOne.java类,这时你就没法合并了,这时你编译的话会报一个错误,报重复类的错误。

想想就知道不可能有两个同名类共存,因为如果你调用的话系统怎么知道你是调用了哪个,所以我们的做法是必须在合并的时候去去掉其中一个MyTestOne.java。其实这里也是说我们可以在gradle中动态的去选着给项目指定使用哪个MyTestOne.java。

有人用过gradle的话肯定知道可以使用 exclude来排除,但是如果我们直接这样写的话

exclude 'com/example/kylin/fristtest/MyTestOne.java'

就会把两个MyTestOne.java都给排除,因为他们的包名是一样的,不信你可以试试。所以我们的做法就是要拿到他在合并之前是属于哪个文件夹的,这时候就需要用到set的java路径,因为这个是属于java的部分

 sourceSets{
        main{
            java {

                srcDirs "src/mtone"
                srcDirs "src/mttwo"

                sourceSets.all{set ->
                    println "${set.name}的文件是 ${set.java.srcDirs}"
                    if(name == "main"){
                        Set myt = java.srcDirs;
                        println myt
                    }
                }

            }
        }
    }

我们直接这样打印看看



可以看出我的结果这里打印出了三个,这样我们就可以拿到自己想保留的路径下的MyTestOne.java,我们可以这样写

sourceSets{
        main{
            java {

                srcDirs "src/mtone"
                srcDirs "src/mttwo"

                sourceSets.all{set ->
                    println "${set.name}的文件是 ${set.java.srcDirs}"
                    if(name == "main"){
                        Set myt = java.srcDirs;
                        println myt
                        myt.each {
                            if(it.name != "mtone"){
                                exclude 'com/example/kylin/fristtest/MyTestOne.java'
                            }
                        }
                    }
                }

            }
        }
    }

拿到set之后可以遍历,然后用name获取具体的路径,这样子就可以排除指定路径下的MyTestOne.java。

但是这里不能获取到多渠道的路径,比如我这样写



我多加了ali和baidu的渠道,这里却获取不了,为什么?
因为这里看到外层



没错已经是指定到main的,如果我们想在ali的渠道下弄,我们需要再写个ali的闭包

我们这样写,把引入的资源放到ali中,可以看到结果


可以看出这里有个很奇怪的问题,没错,就是多渠道的文件,可以不用指定srcDirs,他会自动合并,但是自动合并情况下你用set是无法获取到其他渠道的文件,set只能获取在闭包内srcDirs定义的文件。我觉得这个原因是因为你是从上到下执行时打印的,这时候还没有合并,等你的gradle里面写的代码执行完之后,才开始进行合并操作,所以打印的时候还没开始进行合并。

那怎么办,我还想获取其它渠道的文件的话该怎么办,有两个办法,第一个就是在main中用srcDirs指定渠道的文件,我觉得AS默认合并的操作也是指定srcDirs,但是我没试过这种方法,不知道是否可行。
第二种方法就是用File,没错,我之前说过gradle中可以写java的代码,那我其实也可以使用java中的File类来操作文件。
比如说我想获取到src文件夹下的所有文件,我可以这样写,然后顺便打印看看

File file = file('src')
File[] tempList = file.listFiles()
println "所有文件夹" + tempList

可以看到确实能打印出来,然后你想怎么做,按照java操作File的方式去做就行了。

从这个打印还可以看到一个很有意思的地方,就是他和SourceSets内部打印的顺序是穿插的,我也不知道为什么会这样。

好了,我暂时就只说这么多,还想看详细的SourceSets的操作,可以去看官网的 API ,而如果你想做某些操作而API没有的话,你可以考虑一下用java能不能实现你想要的功能。

还有就是Gradle的内容太多了,而网上很多人讲得都比较浅,上面对SourceSets的操作是我自己在实践中去不断踩坑才总结出来的。所以我只能说Gradle太难了,闭包的思想,还有原理,不同的插件还有不同的操作,而且你在开发时报错的话,查找错误信息还要带点推理的思路去找出错误在哪,说不定你每次运行都会产生不同的错误。最主要是没有找到那种比较去完整理解的教程,所以写这个东西相关的还是挺难的,首先自己就不是很懂,然后还不知道要怎么去组织语言,我这也只能碰到什么写什么了。要是我把这个东西弄懂个七八十,我也不专门写文章,直接写书得了。
我想说的是,如过我对SourceSets的操作能帮到你,那当然是好,如果帮不到你,我也没办法,它的内容还是比较多的,我现在肯定也没法全懂,这东西也没人教我,我去踩坑,能写的我都写了,有些坑我没踩过我也不懂怎么处理。

这篇是补上周的,写gradle组织语言有点难,所以上周没能出一篇

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,265评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,078评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,852评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,408评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,445评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,772评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,921评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,688评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,130评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,467评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,617评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,276评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,882评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,740评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,967评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,315评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,486评论 2 348

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,754评论 25 707
  • Android Studio正式版早已经发布了,默认使用Gradle构建,GitHub上80%的Android项目...
    流哲羽阅读 2,860评论 1 13
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,629评论 18 139
  • 蛋壳的用处实验小学206班 同2018年4月1日 早上,小虫子、小蝴蝶和小蚂蚁用半个蛋壳搭了一个跷跷板。 玩着...
    枭岛阅读 581评论 1 1
  • 大家好,今天要聊的是中西方的法律守护神
    杰瑞喵阅读 998评论 0 1