Gradle 插件入门

Android使用gradle来打包应用越来越普遍了,gradle打包的形式越来越多样化了。butterknife自定义了插件用来生成R2文件。tinker自定义了插件来生成diff。写一个插件可以更加清晰的看到自己打包的流程,同时写好一个插件也需要对打包的流程非常的熟悉。当然这篇文章没有这么的高深,简单的介绍怎么自定义一个插件以及一些简单的打包命令和配置。插件可以做的事情确实太多了。

自定义Gradle Plugin

  • 新建一个java library module。
apply plugin: 'java'
apply plugin: 'groovy'  // gradle library

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile gradleApi()
}

sourceCompatibility = "1.7"
targetCompatibility = "1.7"


group='com.egos.gradle.plugins'
name='pluginsample'
version='0.0.1'
  • 新建一个类(.groovy)继承Plugin,那么一个简单的插件就定义好了。
// 需要创建新的文件夹groovy,将.groovy文件放在里面
package com.egos.gradle.plugins;
class SamplePlugin implements Plugin<Project> {
    protected final Logger log = Logging.getLogger(getClass());

    void apply(Project project) {
        println 'This is a sample plugin.'
    }
}
  • 创建.properties用来指定插件实现类,并发布的在本地(可以调用gradle uploadArchives将plugin发布在本地)。在main目录下面创建resources/META-INF.gradle-plugins/com.egos.gradle.properties
# 最后使用的时候需要com.egos.gradle。
implementation-class=com.egos.gradle.plugins. SamplePlugin

执行gradle uploadArchives就会发布在本地仓库。

apply plugin: 'maven'

group='com.egos.gradle.plugins'
name='pluginsample'
version='0.0.1'

uploadArchives {
    repositories {
        mavenDeployer {
            repository(url: uri('../repo'))
        }
    }
}
  • 使用Gradle Plugin。可以在全局使用(根目录下build.gradle),或者某个module使用。配置完成以后执行打包的时候就可以看到This is a sample plugin.的输出。
buildscript {
    repositories { // 指定仓库地址
        // jcenter() // 如果上传到jcenter的话
        maven { // 本例中上传到了本地的maven中。
            url uri('../repo')
        }
    }
    dependencies {
        classpath 'com.egos.gradle.plugins: pluginsample:0.0.1'
        // 也可以直接指定jar文件的地址使用的,但是无法调试
        // classpath fileTree(dir:'../pluginsample/build/libs', include: ['*.jar']) }
}

apply plugin: 'com.egos.gradle'
  • 高级用法。从上面简单的Demo来看没有任何实际的作用,下面就来一些例子。

(1) 将build.gradle中的代码移到plugin中。作用还是一样的,但是方便调试以及方便书写(build.gradle不能调试,个人感觉效率比较低)。还有如果需要的操作过于复杂,例如生成文件等的时候,写在build.gradle中就更加不方便。

// 在上面SamplePlugin的apply中加入如下代码。
project.afterEvaluate {
    //找到preDebugBuild任务,然后添加一个Action
    project.tasks.getByName("preDebugBuild") {
        it.doFirst {
            println "preDebugBuild doFirst"
        }
    }
}

(2) 定义高级方法。最前面有介绍Tinker可以使用gradle来生成diff。同样的,方便调试以及测试。

调试Gradle Plugin

上面一直在说调试插件,那么插件该如何调试呢?这个问题困扰过自己,一直以来都是通过jar包路径来指定插件的路径的,每次都是通过输出Log信息来查看,找到了一个方法(下文参考中的文章Intellij / Android Studio 调试 Gradle Plugin)。这里也写一下部分内容,其它可以参考上面博客。

  • 首先需要完成上面使用Gradle Plugin操作指定插件地址。如果指定的事远程仓库或者jar地址就无法调试了。
buildscript { repositories { maven { // 本例中上传到了本地的maven中。 url uri('../repo') }
    }
    dependencies { classpath 'com.egos.gradle.plugins: pluginsample:0.0.1' }

apply plugin: 'com.egos.gradle'
  • 选择需要调试的内容。
// 在terminal中输入下面代码。这里的gradlew assembleDebug可以自行选择,也可以是gradle assembleDebug
gradlew assembleDebug -Dorg.gradle.daemon=false -Dorg.gradle.debug=true

常用gradle命令

gradle tasks // 获取task信息
gradle dependencies // 获取以来关系
gradle assembleDebug // 打包Debug
gradle :app:tasks // app(module名字)可以用来区分是哪个module的。每一个任务都可以这样区分。
gradle lint // lint检查

build.gradle常用的配置

  • sourceSets:配置文件目录
android{
    sourceSets {
        main {  // 主工程的文件配置
            manifest.srcFile 'AndroidManifest.xml'
            java.srcDirs = ['src']
            resources.srcDirs = ['src']
            aidl.srcDirs = ['src']
            renderscript.srcDirs = ['src']
            res.srcDirs = ['res']
            assets.srcDirs = ['assets']
            jniLibs.srcDirs = ['libs']
        }

        test { // 主工程java文件配置
            java.srcDirs = ['test/java']
        }

        xxFlavor { // 某个flavor的文件配置(在Build Variant 切换到相应的flavor的时候就会默认使用那个flavor的代码)
            manifest.srcFile 'xxFlovar/AndroidManifest.xml'
            java.srcDirs = ['xxFlovar/java']
            res.srcDirs = ['xxFlovar/res']
        }
}
  • productFlavors:配置不同包
android {
    deft{}  // 默认包
    removeAd{} // 没有广告的包
    ...etc
}
  • signingConfigs:签名配置
adroid {
    signingConfigs {
        config {
            storeFile "key.store"
            storePassword "key.store.password"
            keyAlias "key.alias"
            keyPassword "key.alias.password"
        }
    }
}
  • buildTypes:不同打包方式配置
android {
    buildTypes {
        haha { // 这里其实是可以任意配置的,比如我配置haha,只是默认会有debug和release
            minifyEnabled true
        }
        debug {
            signingConfig signingConfigs.config
        }
        release {
            // 替换Manifest文件中的渠道号
            manifestPlaceholders = [UMENG_CHANNEL_VALUE: "umeng_xx"]
            minifyEnabled true // 混淆开关
            signingConfig signingConfigs.config  // 签名配置
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard.cfg'  // 混淆文件配置
        }   
    }
}

// 替换Manifest文件中的渠道号,类似友盟等等的会在AndroidManifext.xml中配置友盟的一些信息
"UMENG_CHANNEL"
    android:value="umeng_${UMENG_CHANNEL_VALUE}"/>  // manifestPlaceholders会改掉
  • applicationVariants
android {
    applicationVariants.all { variant -> }
}
  • 一些其它重要配置
android {
    defaultConfig {
        multiDexEnabled true  // 开启分dex 
    }
    lintOptions { // 一般会设置false,lint检查出错不提示
        abortOnError false
    }
    dexOptions {
        incremental false
        preDexLibraries = false
        jumboMode = true
    }
    packagingOptions {
        exclude 'META-INF/LICENSE.txt' // 打包时去掉一些内容
        exclude 'META-INF/NOTICE.txt'
    }

    ndk {
        abiFilters 'armeabi' // 过滤ndk的信息,比如这里只会打arm版本的so文件
    }
}

配置编译信息

gradle build 配置信息。例如:gradle build -xx -yy。实际使用的时候需要套一个模版。gradle build -Pxx=xx -Pyy=yy -Pxx是固定的模版/build.gradle中Properties()相当于里面的属性、Project(org.gradle.api.Project,相当于一个工程)。可以在android–defaultConfig–下面配置buildConfigField信息。

// build.gradle文件
// gradle build -Phaha="哈哈"
Properties buildProperties = new Properties();
buildProperties.put("haha", "你好"); // 配置默认的信息

// 获取配置的haha属性
if(project.hasProperty("haha")){  
   buildProperties.put("haha",project.property("haha"))
}
android {
    defaultConfig {
        // 写入到BuildConfig中。
        buildConfigField("String", "haha", "\"" +buildProperties.get("haha") + "\"")
    }
}

思考

想起最开始在2015工程代码从Eclipse迁入到AS的时候,感觉build.gradle是一个很神秘的东西,后来接触了一些插件,自己也写过一些(打包完成以后输出到指定的文件夹等)到现在看源码的时候,很多库都有通过groovy写一些自己的插件。build.gradle不再是一个神秘的东西并且随着配置原来越多、项目越来越大发现它显得太过于臃肿(可能不是源码级别的文件,看起来总是觉得臃肿)。或许将一些比较复杂的任务从build.gradle中脱离处理,写成一个插件是一种更好的解决方案,方便调试,也方便复用。

问题

  • 怎么打纯净版的apk? 实际的项目中总是会有多个flavor,并且可能打包出来的代码有些不一样,比如一个默认包,一个去广告包,但是可以会用初始化的操作,如何打包的时候将这些初始化的操作都去掉呢?

参考

自定义Gradle插件
Intellij / Android Studio 调试 Gradle Plugin
Android官方技术文档翻译——Gradle 插件用户指南(7)

你可能感兴趣的:(groovy)