Android和java构建工具gradle语言深入了解与插件开发

Gradle3.0编程与自动化构建

gradle是一款最新的,功能强大的构建工具,它使用程序代替传统的XML配置,构建项目更加灵活。gradle有丰富的第三方插件。

Gradle相关介绍及开发环境搭建

gradle相关概念介绍

要了解Gradle首先需要了解DSL和groovy与gradle的关系
  • 领域特定语言DSL介绍(gradle属于DSL中一种)

    • 全称domain specific language:它分三类,包含建模语言,sql,html,groovy等

      • 外部DSL

      • 内部DSL

      • 语言工作台

    • 优点

      1、提高开发效率,通过DSL来抽象构建模型,抽取公共的代码,减少重复的劳动;

      2、和领域专家沟通,领域专家可以通过DSL来构建系统的功能;

      3、执行环境的改变,可以弥补宿主语言的局限性。

    • DSL语言与系统编程语言相辅相成,核心思想是求专不求全,解决特定问题

  • groovy详解和初探(Gradle使用的语言就是groovy)

    • 介绍

      • groovy是一种基于JVM的敏捷开发语言

      • 结合了Python、Ruby和Smalltalk的许多强大特性

      • groovy可以与Java完美结合,而且可以使用java所有的库

    • 特性

      • 语法上支持动态类型,闭包等新一代语言特性

      • 无缝集成所有已经存在的java类库

      • 即支持面向对象编程也支持面向过程编程

  • groovy优势

    • 一种更加敏捷的编程语言

    • 入门非常的容易,但是功能非常的强大

    • 既可以作为编程语言也可以作为脚本语言

    • 有java基础很容易上手groovy

各系统平台下gradle开发环境搭建及工程创建

  • mac/linux环境下,groovy开发环境搭建

    • 安装jdk

    • 到官网下载groovy,解压到合适位置

    • 配置groovy环境变量

    • 输入groovy -version测试

  • windows环境下groovy开发环境搭建

    • 安装jdk

    • 到官网下载groovy,解压到合适位置

    • 配置groovy环境变量

    • 输入groovy -version测试

  • IntelliJ IDEA开发工具安装及groovy环境配置

    • 下载安装idea基础版(无需破解支持groovy)

    • 旧版本安装groovy插件

  • 在idea中创建一个groovy工程

    • 创建项目,选择groovy,指定groovy和jdk目录

    • 新建groovy.class,groovy不仅能写java,也能写脚本语言

      class HelloGroovy{
          static void main(args){
              println "hello groovy"
          }
      }
      

Gradle核心语法详解及实践

groovy基本类型会自动转为对象类型

字符串特殊用法

  • String

    • 可以通过def来定义
  • GString

    • 常用的三种定义方式

      • 一般使用单引号:单引号是普通字符串,类似java中的双引号字符串

      • 双引号:可扩展字符串,通过变量来拼接--》def sayHello="hello: ${n}" ,这时它的类型是GStringImpl

      • 三引号:预输出字符串,类似html中p标签

    • 新增字符串操作

      • 字符串扩展可以字符串和任意表达式拼接
    • 新增API讲解

      • println str.center(8,'a''):表示用a填充字符串,原字符串在中间

      • println str.padLeft(8,'a')向左添加至字符串长度为8位,也有向右添加方法

      • 字符串可以直接通过大于小于号进行比较,也可以通过compareTo()函数比较

      • 字符串获取其中的:str[0],也可以str.getAt(0),可以获取多个-》str[0..1]

      • 字符串减法:str.minus(str2)也可以直接用减号-》str-str2

      • 字符串倒序操作:str.reverse()

      • str.capitalize():首字母大写

      • 判断是否是int类型字符串:str.isNumber()

      • 字符串直接转类型:str.toDouble()

gradle常见数据结构包含(list,map,range...)使用

  • 逻辑控制

    • 顺序逻辑:单步往下执行

    • 条件逻辑:if/else,swtch/case

      • if/else和其它语言使用基本一样

      • switch/case

        • 支持任意类型的case(list,range,string,int,double),列表中包含的时候会匹配到
    • 循环逻辑:while,for

      • while和其它语言使用基本一样

      • for:可以使用范围0..9特有数据结构

         def sum=0
         //范围循环
        
         for (i in 0..9){
        
             sum+=i
        
         }
         //循环list,list在这更像一个数组
         for (i in [1,2,3,4,56,78,9]){
             sum +=i
        
         }
         //map循环
          for (i in ['lili':1,'luck':3,'xiaoming':5]){
             sum+=i.value
        
          }
        
  • 闭包

    • groovy闭包基础

      • 闭包概念

        • 闭包定义:闭包可以说是定义在函数中的函数,其实闭包就是一个代码块

          //定义闭包
          def clouser = {println 'hello groovy'}
          clouser.call()//简单执行
          
        • 闭包调用:clouser.call(‘will’)/clouser()

      • 闭包参数

        //带参数闭包,这里使用双引号可以取出参数并拼接
        def clouser1 = {String name ->println "hello groovy ${name}"}
        
      • 闭包返回值

        def clouser = {return "hell ${it}"}//这里it表示输出默认参数,return返回值
        
    • groovy闭包使用-》常用的

      • 与基本类型的结合使用

        int x=fab(5)
        println x
        //结合upto函数,实现阶乘
        int fab(int number){
            int result=1
            1.upto(number,{num->result*=num})
            return result
        }
        //结合downto函数实现阶乘
        int fab2(int number){
        
            int result =1
            number.downto(1){
                num->result*=num
            }
            return result
        }
        //闭包累加
        int z=numAdd(6)
        println z
        int numAdd(int number){
            int result =0
            number.times {
                num -> result+= num
            }
            return result
        }
        
      • 与String结合使用

        Str
        ing str ='the 2 and 3 is 5'
        //字符串遍历,方法后面加闭包
        str.each {
            String temp -> print temp+','
        }
        //find查找符合条件的第一个
        println str.find{
            String s-> s.isNumber()
        }
        //闭包查找所有字符串
        def list=str.findAll{String s-> s.isNumber()}
        println list.toListString()
        //any方法和闭包判断是否包含数字
        def result=str.any {
            String s-> s.isNumber()
        }
        println result
        str.every//此方法是所有都符合返回ture,否则false
        //闭包遍历字符串转换大写
        def listToUpper=str.collect {it.toUpperCase()}
        
      • 闭包进阶

      • 与数据结构结合使用

      • 与文件结合使用

    • 闭包进阶<a id="clouser" href="#"></a>

      • 闭包的关键变量

            def scriptClouser={
            println "scriptClouser this:"+this
            println "scriptClouser owner:"+owner
            println "scriptClouser delegate:"+delegate
           }
           //三个变量输出结果一样
        
            scriptClouser.call()
        
        • this:代表闭包定义处的类

        • owner:代表闭包定义处的类或者对象

        • delegate:代表任意对象,默认值与owner一致

        在类中定义一个闭包,this、owner、delegate三个对象一致;在闭包中定义闭包,this和其它两个就不同;delegate对象被修改后会和owner不同
        class Person{
            def classClouser={
                println "classClouser this:"+this
                println "classClouser owner:"+owner
                println "classClouser delegate:"+delegate
            }
        }
        Person p=new Person()
        p.classClouser.call()
        //闭包中定义闭包
        def nestClouser={
            def innerClouser={
                println "innerClouser this:"+this
                println "innerClouser owner:"+owner
                println "innerClouser delegate:"+delegate
            }
            innerClouser.delegate=p//修改默认的delegate
            innerClouser.call()
        }
        
      • 闭包委托策略

        • 有四种委托策略(OWNER_FIRST,OWNER_ONLY,DELEGATE_FIRST,DELEGATE_ONLY),默认委托策略是OWNER_FIRST
        class Person{
            String name
            def pretty ={"My name is ${name}"}
            String toString() {
                pretty.call()
            }
            def classClouser={
                println "classClouser this:"+this
                println "classClouser owner:"+owner
                println "classClouser delegate:"+delegate
            }
        }
        class Teacher{
            String name
        }
        Person p=new Person()
        p.classClouser.call()
        def nestClouser={
            def innerClouser={
                println "innerClouser this:"+this
                println "innerClouser owner:"+owner
                println "innerClouser delegate:"+delegate
            }
            innerClouser.delegate=p//修改默认的delegate
            innerClouser.call()
        }
        def stu=new Person(name:'xiaoming')
        def tea=new Teacher(name:'lili')
        stu.pretty.delegate=tea//修改默认delegate
        stu.pretty.resolveStrategy=Closure.DELEGATE_FIRST//指定闭包的委托策略
        println '输出'+stu.toString()
        

    闭包数据结构
  • 列表

    • 列表的定义

      def list=[1,2,-3,4,5,6]//定义和初始化,默认是ArrayList
      def list=[1,2,3,4,5,6] as int[]//int类型数组,也可以和java一样定义
      //groovy数组操作和list一样
      
    • 列表的操作

      • list.addAll([10,15])
        list.add(6)
        
      • assert [1,'2',3,1,1,1].removeAll(['2',3])
        assert list.remove(2)
        assert list.removeAll([4,5])
        list.removeAt(1)
        print list
        
      • //循环list,list在这更像一个数组
         for (i in [1,2,3,4,56,78,9]){
          print i
         }
         //简洁循环
         [1,2,3].each{it -> store += it}
         //find查找符合条件的第一个
         println str.find{
         String s-> s.isNumber()
        }
        //查找所有奇数
        def findList=[-3,9,6,2,-7,1,5]
        def result=findList.findAll{return it%2!=0}
        print result
        //any和every方法
        findList.any{return it%2!=0}
        findList.every{return it%2!=0}
        //查找最大和最小值;也可以闭包指定规则
        findList.min()
        findList.max{return Math.abs(it)}//闭包绝对最大值
        findList.count()
        
      • def list=[1,2,-3,4,5,6]
        Comparator mc={
            a,b-> a==b? 0:Math.abs(a)return it.size()}//按照字符串长度排序
        
  • map与map类型映射

    def colors=[red:'ff0000',green:'00ff00',blue:'0000ff']
    //索引方式
    println colors['red']
    colors.red
    //添加元素
    colors.leftShift(white:"fffff")
    colors.yellow='ffff00'
    colors.complex=[a:1,b:2]//可以添加任意元素到map
    println colors.toMapString()
    println colors.getClass()//LinkedHashMap默认是LinkedHashMap,可以通过as关键字修改映射类型
    //遍历
    colors.each {
        def color->
            println "this key is ${color.key},"+
                    "the value is ${color.value}"
    }
    colors.eachWithIndex{ def entry, int i ->
        println "this key is ${entry.key},"+
            "this index is ${i},"+
                "the value is ${entry.value}"
    }
    colors.each {
        def key,def value->
            println "this key is ${key},"+
                    "the value is ${value}"
    }
    //查找
    def entry=colors.find {def c->
        return c.value.equals('0000ff')
    }
    println entry
    
    def students=[
            1:[number:'001',name:'Bob',score:55,sex:'male'],
            2:[number:'002',name:'Amey',score:60,sex:'male'],
            3:[number:'003',name:'Leo',score:77,sex:'male'],
            4:[number:'004',name:'Habby',score:99,sex:'male']
    ]
    //强大的函数直接分组
    def group=students.groupBy {def student->
        return student.value.score>=60? '及格':'不及格'
    }
    println group.toMapString()
    
  • 范围

    • 范围的基本概念:范围继承list,但是比list简单点

    • 范围操作:

      def range=1..10
      println range[0]
      println range.contains(10)
      println range.from
      println range.to
      //遍历
      range.each {
          print it
      }
      def result=getGrade(75)
      println result
      def getGrade(Number number){
          def result
          switch (number){
              case 0..<60:
                  result='不及格'
                  break
              case 60..<70:
                  result='及格'
                  break
              case 70..<80:
                  result='良好'
                  break
              case 80..<100:
                  result='优秀'
                  break
          }
          return result
      }
      

gradle面向对象特性

  • groovy中类,接口等的定义和使用
//class Person implements Action{//实现接口
class Person implements DefaultAction{//实现Trait类

    String name
    Integer age
    def increaseAge(Integer years){
        this.name+=years
    }
    @Override
    void eat() {
    }
    @Override
    void drink() {
    }

    @Override
    void play() {

    }
}
//定义接口
interface Action {
    void eat()
    void drink()
    void play()

}
//定义trait类型,类似接口,但是可以有实现类
trait DefaultAction {
    abstract void eat()
    void play(){
        println 'i can play'
    }
}
  • groovy中的元编程

    • groovy在执行方法的时候先判断类中是否有此方法-》有调用,没有-》判断MetaClass中是否有此方法-》判断是否重写了methodMissing()方法-》判断是否重写了InvokeMethod方法-》以上没通过抛出MissingMethodException

      //class Person implements Action{
      class Person  {
          String name
          Integer age
          def increaseAge(Integer years){
              this.name+=years
          }
          //一个方法找不到时调用它来代替,否则抛出MissingMethodException
          def invokeMethod(String name,Object args){
              return "the method is ${name},the params is ${args}没有此方法"
          }
          //一个方法找不到时先调用这个方法然后调用invokeMethod方法
          def methodMissing(String name,Object args){
              return "this method ${name} is missing"
          }
      }
      
      def person=new Person(name:"will",age:25)
      //println person.age
      println person.cry()//调用一个不存在的方法
      //为类动态添加一个属性
      Person.metaClass.sex='male'
      //为类动态添加方法
      Person.metaClass.sexUpperCase={-> sex.toUpperCase()}
      def person2=new Person(name:"will",age:25)
      println person2.sexUpperCase()
      //为类动态添加静态方法
      Person.metaClass.static.createPerson={
          String name,int age->new Person(name:name,age:age)
      }
      def person3=Person.createPerson("Amey",30)
      println person3.name
      

Gradle高级用法实践

gradle中json文件处理及json,model互转,网络json处理

import groovy.json.JsonOutput
import groovy.json.JsonSlurper

def list=[new Person(name:'John',age:25)
        ,new Person(name: 'mary',age:26)]
def jsonStr= JsonOutput.toJson(list)
println jsonStr
println JsonOutput.prettyPrint(jsonStr)
def jsonSlpuer=new JsonSlurper()
//jsonSlpuer.parse()
println jsonSlpuer.parseText(jsonStr)
//从网络获取json并解析
def response=getNetWorkData('https://www.sojson.com/open/api/lunar/json.shtml')
println response.data.suit//输出网络数据
//获取数据返回对象
def getNetWorkData(String url){
    //发送http请求
    def connection=new URL(url).openConnection()
    connection.setRequestMethod('GET')
    connection.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)")
    connection.connect()
    def responese=connection.content.text
    //将json对象转化为实体对象
    def jsonSluper=new JsonSlurper()
    return jsonSluper.parseText(responese)
}

xml文件读取和生成

import groovy.xml.MarkupBuilder

final String xml='''

NOAA's National Weather Service
http://weather.gov/

http://weather.gov/images/xml_logo.gif
NOAA's National Weather Service
http://weather.gov

New York/John F. Kennedy Intl Airport, NY
KJFK
40.66
-73.78
Mon, 11 Feb 2008 06:51:00 -0500 EST

A Few Clouds
11
-12
36
West
280
18.4
29
1023.6
30.23
-11
-24
-7
-22
10.00

http://weather.gov/weather/images/fcicons/
nfew.jpg
http://weather.gov/disclaimer.html
http://weather.gov/disclaimer.html

'''
//开始解析xml数据
def xmlSluper=new XmlSlurper()
def response=xmlSluper.parseText(xml)
println response.current_observation.credit
//自带深度遍历
def taglist=response.depthFirst().findAll {
    wind_dir ->
        return wind_dir.text
}
println taglist
//children()方法实现广度遍历
//生成xml
def sw=new StringWriter()
def xmlBuilder=new MarkupBuilder(sw)//用来生成xml文件的核心类
//根节点langs创建
xmlBuilder.langs(type:'current',count:'3',mainstream:'true'){
    //第一个language节点
    language(flavor:'static',version:'1.5',value:'java'){
        age('24')
    }
    language(flavor:'dynamic',version:'1.5',value:'groovy'){
        age('16')
    }
    language(flavor:'dynamic',version:'1.5',value:'javaScript')
}
println sw
//对象生成xml
def langs=new Langs()
xmlBuilder.langs(type:langs.type,count:langs.count,mainstream:langs.mainstream){
    langs.languages.each {lang->
        languge(flavor:lang.flavor,
        version:lang.version,lang.value)
    }
}
println sw
class Langs{
    String type='current'
    int count =3
    boolean mainstream=true
    def languages=[new Language(flavor: 'static',version: '1.5',value: 'java'),
                   new Language(flavor: 'static',version: '17',value: '.c++'),
                   new Language(flavor: 'dynamic',version: '8.0',value: 'javaScript'),
                   new Language(flavor: 'dynamic',version: '3.5',value: 'python'),
                   new Language(flavor: 'dynamic',version: '2.4',value: 'groovy'),
    ]
}
class Language{
    String flavor
    String version
    String value
}

普通文件的读写,文件下载功能实现

def file=new File('../HelloGroovy.iml')
def text=file.getText()//获取文件内容
//def result=file.readLines()
println text
//拷贝文件实现
def result=copyFile("../HelloGroovy.iml",'../HelloGroovy2.text')
println result
def copyFile(String sourcePath,String destationPath){
    try{
        //创建目标文件
        def desFile=new File(destationPath)
        if(!desFile.exists()){
            desFile.createNewFile()
        }
        new File(sourcePath).withReader {reader->
            def lines=reader.readLines()
            desFile.withWriter {writer->
                lines.each {line->
                    writer.append(line+'\r\n')
                }
            }
        }
        return true
    }catch(Exception e){
        e.printStackTrace()
    }
}
def person=new Person(name: 'Cherry',age:19)
//saveObject(person,"../person.bean")
def objResult=readObject("../person.bean")
println objResult
//存储类到文件
def saveObject(Object object,String path){
    try{
        //创建目标文件
        def desFile=new File(path)
        if(!desFile.exists()){
            desFile.createNewFile()
        }
        desFile.withObjectOutputStream {out->
            out.writeObject(object)
        }
        return true
    }catch(Exception e){
        e.printStackTrace()
    }
    return false
}
//在文件中读取类
def readObject(String path){
    def obj=null
    try{
        def file=new File(path)
        if(file==null||!file.exists())return null
        //从文件中读取对象
        file.withObjectInputStream{input->
            obj=input.readObject()
        }
    }catch(Exception e){

    }
    return obj
}
groovy与java对比
  • 写法上:没有java那么多限制

  • 功能上:java中的类和方法都可以直接用,对java的功能扩展后,groovy实现功能更方便简洁

  • 作用上:既可以编写应用,也可以编写脚本

Gradle概述

gradle是什么,gradle能做什么

  • gradle不仅仅是构建工具,也可以看做是一种编程框架

    • gradle组成部分

      • groovy核心语法

      • build script block

      • gradle api

    • 优势

      • 更加灵活:Maven和ant不能自己修改构建过程,gradle可以写构建脚本

      • 粒度上:gradle开源,构建每一步都清晰可见,可修改

      • 扩展性:支持插件,可以复用插件

      • 兼容性:gradle吸取了所有构建工具的长处

    • gradle执行分三阶段

      • initialization初始化阶段:解析整个工程中的所有project,构建所有project对应的project对象

      • Configuration配置阶段:解析所有project对象中的task,构建好所有task的拓扑图

      • Execution执行阶段:执行具体的task及其依赖task

        /*Android gradle构建阶段监听*/
        /*首先在环境变量配置好gradle,具体百度*/
        /*在Terminal终端输入-gradlew clean测试*/
        /*在project下的build.gradle中添加如下代码*/
        /*配置阶段开始前的回调监听*/
        this.beforeEvaluate{
            println '开始'
        }
        /*配置阶段完成以后的回调监听*/
        this.afterEvaluate{
            println '完成'
        }
        /*gradle执行完毕后的回调监听*/
        this.gradle.buildFinished{
            println '结束'
        }
        /*在setting.gradle中输出*/
        println '初始化阶段开始'
        /*执行gradlew clean查看终端输出*/
        

Gradle核心类之Project详解及实践

projectAPI组成
  • gradle生命周期api

    /*配置阶段开始前的回调监听*/
    this.beforeEvaluate{
        println '开始'
    }
    /*配置阶段完成以后的回调监听*/
    this.afterEvaluate{
        println '完成'
    }
    /*gradle执行完毕后的回调监听*/
    this.gradle.buildFinished{
        println '结束'
    }
    /*project相关api*/
    this.getProjects()
    def getProjects(){
        println '--------------------'
        println 'Root project'
        println '--------------------'
        this.getAllprojects().eachWithIndex{Project project,int i->
            if(i==0){
                println "Root project--- :${project.name}"
            }else {
                println "+---*** project: ${project.name}"
            }
        }
    }
    
  • project相关api

    /*project api讲解*/
    project('app'){Project project->
    //    println project.name
        applay plugin:'com.android.application'
        dependencies {}
        android {}
    }
    /*配置当前工程和其所有子工程*/
    allprojects{
        group 'com.will'
        version '1.0.0=release'
    }
    /*配置所有子工程*/
    subprojects{ Project project->
        if(project.plugin.hasPlugin('com.android.library')){
            applay from:'../publishToMaven.gradle'//引入Maven配置文件
        }
    }
    
  • task相关api

  • 属性相关api

    /*自定义属性*/
    def mCompileSdkVersion =26
    def mLibAndroidSupportAppcompatV7 ='com.android.support:appcompat-v7:26.+'
    /*通用扩展属性,在根build中定义一次,所有子moudle都可以使用*/
        ext {
         compileSdkVersion =26
         libAndroidSupportAppcompatV7 ='com.android.support:appcompat-v7:26.+'
        }
     /*在moudel中使用*/
     android {
        compileSdkVersion this.rootProject.compileSdkVersion
        buildToolsVersion "26.0.3"
        ...
    }
    /*因为继承关系也可以直接调用*/
    dependencies { compile this.libAndroidSupportAppcompatV7
    }
    /**
    我们可以把所有属性通过扩展属性的方式定义到一个新的common.gradle中,在根gradle中通过
    apply from:this.file('common.gradle')来引用
    */
    ![5c846d0725918](https://i.loli.net/2019/03/10/5c846d0725918.png)
    //moudle中使用
    android{
        compileSdkVersion rootProject.ext.android.compileSdkVersion
    }
    //在gradle.properties中定义属性
    isLoadTest=false
    isLoadPullRefresh=true
    mCompileSdkVersion=26
    //使用,在setting.gradle
    include ':app'
    println '初始化阶段开始'
    if(hasProperty('isLoadPullRefresh')? isLoadPullRefresh.toBoolean():false){
        include  ':pullToRefresh'
    }
    //在moudel下gradle 
    compileSdkVersion mCompileSdkVersion.toInteger()
    
    • common.gradle

      5c846d0725918
      5c846d0725918
  • file相关api

    • 路径获取相关api

      println "根目录路径"+getRootDir().absolutePath
      println "build下路径"+getBuildDir().absolutePath
      println "project的目录"+getProjectDir().absolutePath
      
    • 文件操作相关api

      /*gradle拷贝文件,可以拷贝整个目录*/
      copy {
          from file('app.iml')
          into getRootProject().getBuildDir()
          //此方法闭包过滤不想拷贝的文件
          exclude{}
      }
      //文件树遍历
      fileTree('build/outputs/apk/'){FileTree fileTree->
          fileTree.visit{FileTreeElement element->
              println 'the file name is:'+element.file.name
              copy {
                  from element.file
                  into getRootProject().getBuildDir().path+'/test/'
              }
          }
      }
      
  • 其它api

    
      //当有依赖包冲突时可以使用此命令去除冲突的部分 
      compile this.libAndroidSupportAppcompatV7{
            exclude module:'support-v4'
            //排除指定包下的所有库
            exclude group:'com.android.support'     
            transitive false//禁止传递依赖,是否使用引入的库的第三方依赖       
    
      }
    
    //compile 依赖打包时会把依赖的类和资源打包到apk,provided依赖编译时使用依赖资源,打包时不会打包依赖资源,provided会避免重复引用,尽量使用provided
    
    //使用外部命令
    task(name:'apkcopy'){
        doLast{
            //在gradle执行阶段去执行
            def sourcePath=this.buildDir.path+'outputs/apk'
            def desationPath='users/Andministrotor/Download/'
            def command="mv -f ${sourcePath} ${desationPath}"
            exec {
                try{
                    executable 'bash'
                    args '-c', command
                    println 'the command is execute success'
                }catch(Exception e){
                    println 'the command is execute failed'
                }
            }
        }
    }
    //buildscript配置maven仓库
    buildscript { ScriptHandler scriptHandler->
        //配置我们的仓库地址
        scriptHandler.repositories { RepositoryIdHelper repositoryIdHelper->
            repositoryIdHelper.jcenter()
            mavenCentral()
            mavenLocal()
            //私有的maven仓库配置,可以配置多个
            maven {
                name 'personal'
                url 'http://localhost:8081:/nexus/repositories'
                creadentials{
                    username ='admin'
                    password = 'admin123'
                }
            }
        }
    
        //buildscript中的此方法配置我们工程的'插件'依赖地址
        dependencies {
            //此插件指定项目为Android项目
    //        classpath 'com.android.tools.build:gradle.2.2.2'
            classpath 'com.tencent.tinker-patch-gradle-plugin:1.7.7'//引入腾讯热修复框架
        }
    
    }
    

Gradle核心之task详解及实践

task定义及使用,task执行流程

使用gradle task命令查看当前项目的task
//创建task,配置组名和描述
task helloTask(group:'will',description:'task study'){
    println 'i am helloTask'
    doFirst{//可以多次调用,在gradle的执行周期执行,原有的task之前执行
        println 'the task group is:'+group
    }
    doLast{}//原有的task之后执行

}
helloTask.doFirst{//外部调用先执行
    println 'the task description is'+description
}
//通过TaskContainer创建task
this.tasks.create(name:'helloTask2'){
    //使用set设置组名
    setGroup('will')
    println 'i am helloTask2'
}

//计算build执行时长的功能
def startBuildTime,endBuildTime
this.afterEvaluate{Project project->
    //保证要找的task已经执行完毕
    def preBuildTask=project.tasks.getByName('preBuild')
    preBuildTask.doFirst {//在此task之前执行
        startBuildTime=System.currentTimeMillis()
        println 'the startTime is:'+startBuildTime
    }
    def buildTask=project.tasks.getByName('build')
    buildTask.doLast{
        endBuildTime=System.currentTimeMillis()
        println "the build time is:${endBuildTime-startBuildTime}"
    }
}
5c8498db9288c
5c8498db9288c

task依赖关系与输入输出,task继承与实现

task taskX {
    doLast {
        println 'taskX'
    }
}
task taskY {
    doLast {
        println 'taskY'
    }
}
task taskZ(dependsOn: [taskX,taskY]) {
    dependsOn  this .tasks.findAll{ task->
        return task.name.startsWith('lib')//动态指定依赖所有lib开头的
    }
    doLast {
        println 'taskZ'
    }
}
task lib1 <<{println 'lib1'}
task lib2 <<{println 'lib2'}

//task生成版本说明文件
task handlerReleaseFile {
    def srcFile=file('releases.xml')
    def destDir=new File(this.buildDir,'generated/release/')
    doLast{
        println '开始解析对应的xml...'
        destDir.mkdir()
        def releases=new XmlParser().parse(srcFile)
        releases.release.each{ releaseNode->
            //解析每个release节点的内容
            def name=releaseNode.name.text()
            def versionCode=releaseNode.versionCode.text()
            def versionInfo=releaseNode.versionInfo.text()
            //创建文件并写入节点数据
            def destFile=new File(destDir,"release-${name}.text")
            destFile.withWriter{ writer->
                writer.write("${name} -> ${versionCode} -> ${versionInfo}")
            }
        }
    }
}
task handlerReleaseFileTest(dependsOn:handlerReleaseFile){
    def dir =fileTree(this.buildDir.path+'generated/release/')
    doLast {
        dir.each {
            println 'the file name is:'+it
        }
        println '输出完成...'
    }
}

task修改默认构建流程,task源码解读

通过脚本把构建版本信息写入xml,然后在把写入方法添加到构建过程中
//生成版本信息xml和读取,在构建流程中加入添加版本信息到xml
//实际开发中可以把一个独立的gradle功能代码写入单独的gradle文件,然后引入
ext {

    versionName = '1.0.0'

    versionCode = '100'

    versionInfo = 'App的第一个版本,上线了一些最基础的核心功能'

    destFile = file('releases.xml')

    if (destFile != null && !destFile.exists()) {

        destFile.createNewFile()

    }

}

task writeTask {

    //为task指定输入

    inputs.property('versionCode', this.versionCode)

    inputs.property('versionName', this.versionName)

    inputs.property('versionInfo', this.versionInfo)

    //为task指定输出

    outputs.file destFile

    doLast {

        def data = inputs.getProperties()

        File file = outputs.getFiles().getSingleFile()

        def versionMsg = new VersionMsg(data)

        def sw = new StringWriter()

        def xmlBuilder = new MarkupBuilder(sw)

        if (file.text != null && file.text.size() <= 0) {

            xmlBuilder.release {

                release {

                    versionCode(versionMsg.versionCode)

                    versionName(versionMsg.versionName)

                    versionInfo(versionMsg.versionInfo)

                }

            }

            file.withWriter {writer->

                writer.append(sw.toString())

            }

        }else{

            xmlBuilder.release {

                versionCode(versionMsg.versionCode)

                versionName(versionMsg.versionName)

                versionInfo(versionMsg.versionInfo)

            }

            //将生成的数据插入到根节点之前

            def lines=file.readLines()

            def lengths=lines.size()-1

            file.withWriter {writer->

                lines.eachWithIndex{ String line, int index ->

                    if(index!=lengths){

                        writer.append(line+'\r\n')

                    }else if(index==lengths){

                        writer.append('\r\r\n'+sw.toString() +'\r\n')

                        writer.append(lines.get(lengths))

                    }

                }

            }

        }

    }

}

task readTask {

    inputs.file this.destFile

    doLast {

        def file=inputs.files.singleFile

        println file.text

    }

}

task taskTest {

    dependsOn readTask ,writeTask

//    shouldRunAfter

//    mustRunAfter

    doLast {

        println '输入输出任务结束'

    }

}

class VersionMsg {

    String versionCode

    String versionName

    String versionInfo

}
//在构建流程中加入添加版本信息到xml
this.project.afterEvaluate {project->

    def buildTask=project.tasks.getByName("build")

    if(buildTask==null){

        throw GradleException('the build task is not found')

    }

    buildTask.doLast {

        writeTask.execute()//每次构建项目时把版本信息写入xml文件

    }

}
通过查看腾讯tinker源码我们可以知道它把自己的方法插入到gradle构建流程中,在processReleaseManifest之后,在processReleaseResource之前
5c84d0cf851d0
5c84d0cf851d0
为了自定义task方便,官方文档提供了很多不同类型的task
5c84d143ce2a3
5c84d143ce2a3

自动化实现工程插件更新功能

Gradle核心之其它模块详解与实践

第三方库依赖管理及gradle如何处理依赖原理详解

工程初始化核心类Setting类作用及自定义

  • settings类在setting.gradle中体现其作用,通过include 引入子工程,并且可以通过自定义属性判断什么条件引入某个工程

源码管理类SourceSet详解及实际工作中的妙用

  • sourceSet类按照约定来管理java中的类和资源,默认从java下获取源码,从res获取资源进行编译;其主要作用就是管理资源、源码、库;Android时通过AndroidSourceSet类管理,java通过javaSourceSet管理

  • sourceSet修改so库即jni默认目录

     sourceSets {
    
            main {
    
                //修改默认so库jni目录到libs下
    
                jniLibs.srcDirs=['libs']
    
            }
    
        }
    
  • SourceSet修改默认的资源文件路径为多路径,对资源进行分类

    5c84d787e1a8b
    5c84d787e1a8b

插件类plugin

自定义plugin需要继承plugin类
  • 首先创建一个plugin工程,工程目录结构如下,代码在groovy中,引入配置路径在resources中

    5c85205e2b6ad
    5c85205e2b6ad
    //plugin工程gradle文件
    apply plugin: 'groovy'
    
    sourceSets {
    
        main {
    
            groovy {
    
                srcDir 'src/main/groovy'
    
            }
    
            resources {
    
                srcDir 'src/main/resources'
    
            }
    
        }
    
    }
    
    //资源文件下配置引入路径代码
    implementation-class=com.will.gradle.study.GradleStudyPlugin
    //自定义插件groovy类,需要继承plugin类
    class GradleStudyPlugin implements Plugin {
    
        /**
    
         * 唯一需要实现的就是这个方法,参数就是引入了当前插件的Project对象
    
         * @param project
    
         */
    
        @Override
    
        void apply(Project project) {
    
            println '这里导入了自定义插件'
    
            //创建扩展属性
    
            project.extensions.create('imoocReleaseInfo',
    
                    ReleaseInfoExtension)
    
            //创建Task
    
            project.tasks.create('imoocReleaseInfoTask',
    
                    ReleaseInfoTask)
    
        }
    
    }
    //自定义ReleaseInfoExtension类,与自定义plugin进行参数传递
    class ReleaseInfoExtension {
    
        String versionCode
    
        String versionName
    
        String versionInfo
    
        String fileName
    
        ReleaseInfoExtension() {
    
        }
    
        @Override
    
        String toString() {
    
            """| versionCode = ${versionCode}
    
               | versionName = ${versionName}
    
               | versionInfo = ${versionInfo}
    
               | fileName = ${fileName}
    
            """.stripMargin()
    
        }
    
    }
    //引入plugin
    apply plugin: 'com.will.gradle.study'
    //在app下gradle 中调用plugin定义的方法传递参数
    imoocReleaseInfo {
    
        versionCode = rootProject.ext.android.versionCode
    
        versionName = rootProject.ext.android.versionName
    
        versionInfo = '第八个版本。。。'
    
        fileName = 'releases.xml'
    
    }
    //在plugin中自定义task,在gradle执行过程中把版本信息写入xml
    package com.imooc.gradle.study
    
    import groovy.xml.MarkupBuilder
    
    import org.gradle.api.DefaultTask
    
    import org.gradle.api.tasks.TaskAction
    
    /**
    
     * 自定义Task,实现维护版本信息功能
    
     */
    
    class ReleaseInfoTask extends DefaultTask {
    
        ReleaseInfoTask() {
    
            group = 'imooc'
    
            description = 'update the release info'
    
        }
    
        /**
    
         * TaskAction注解的代码执行于gradle执行阶段的代码
    
         */
    
        @TaskAction
    
        void doAction() {
    
            updateInfo()
    
        }
    
        //真正的将Extension类中的信息呢,写入指定文件中
    
        private void updateInfo() {
    
            //获取将要写入的信息
    
            String versionCodeMsg = project.extensions.
    
                    imoocReleaseInfo.versionCode
    
            String versionNameMsg = project.extensions.
    
                    imoocReleaseInfo.versionName
    
            String versionInfoMsg = project.extensions.
    
                    imoocReleaseInfo.versionInfo
    
            String fileName = project.extensions.
    
                    imoocReleaseInfo.fileName
    
            def file = project.file(fileName)
    
            //将实体对象写入到xml文件中
    
            def sw = new StringWriter()
    
            def xmlBuilder = new MarkupBuilder(sw)
    
            if (file.text != null && file.text.size() <= 0) {
    
                //没有内容
    
                xmlBuilder.releases {
    
                    release {
    
                        versionCode(versionCodeMsg)
    
                        versionName(versionNameMsg)
    
                        versionInfo(versionInfoMsg)
    
                    }
    
                }
    
                //直接写入
    
                file.withWriter { writer -> writer.append(sw.toString())
    
                }
    
            } else {
    
                //已有其它版本内容
    
                xmlBuilder.release {
    
                    versionCode(versionCodeMsg)
    
                    versionName(versionNameMsg)
    
                    versionInfo(versionInfoMsg)
    
                }
    
                //插入到最后一行前面
    
                def lines = file.readLines()
    
                def lengths = lines.size() - 1
    
                file.withWriter { writer ->
    
                    lines.eachWithIndex { line, index ->
    
                        if (index != lengths) {
    
                            writer.append(line + '\r\n')
    
                        } else if (index == lengths) {
    
                            writer.append('\r\r\n' + sw.toString() + '\r\n')
    
                            writer.append(lines.get(lengths))
    
                        }
    
                    }
    
                }
    
            }
    
        }
    
    }
    

Jenkins持续集成

Jenkins持续集成步骤
  • 创建jenkins环境变量

  • 下载jenkins.war包

  • 执行java -jar jenkins.war,安装jenkins到环境变量指定的位置,期间会有密码输出

  • 输入localhost:8080 访问网页进入jenkins,输入密码配置jenkins

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