Android使用gradle来打包应用越来越普遍了,gradle打包的形式越来越多样化了。butterknife自定义了插件用来生成R2文件。tinker自定义了插件来生成diff。写一个插件可以更加清晰的看到自己打包的流程,同时写好一个插件也需要对打包的流程非常的熟悉。当然这篇文章没有这么的高深,简单的介绍怎么自定义一个插件以及一些简单的打包命令和配置。插件可以做的事情确实太多了。
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,将.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.'
}
}
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'))
}
}
}
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'
(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。同样的,方便调试以及测试。
上面一直在说调试插件,那么插件该如何调试呢?这个问题困扰过自己,一直以来都是通过jar包路径来指定插件的路径的,每次都是通过输出Log信息来查看,找到了一个方法(下文参考中的文章Intellij / Android Studio 调试 Gradle Plugin)。这里也写一下部分内容,其它可以参考上面博客。
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 tasks // 获取task信息
gradle dependencies // 获取以来关系
gradle assembleDebug // 打包Debug
gradle :app:tasks // app(module名字)可以用来区分是哪个module的。每一个任务都可以这样区分。
gradle lint // lint检查
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']
}
}
android {
deft{} // 默认包
removeAd{} // 没有广告的包
...etc
}
adroid {
signingConfigs {
config {
storeFile "key.store"
storePassword "key.store.password"
keyAlias "key.alias"
keyPassword "key.alias.password"
}
}
}
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会改掉
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中脱离处理,写成一个插件是一种更好的解决方案,方便调试,也方便复用。
自定义Gradle插件
Intellij / Android Studio 调试 Gradle Plugin
Android官方技术文档翻译——Gradle 插件用户指南(7)