杂篇:一代版本一代神[-Gradle-]
零、前言
本文主要包括: |---Gradle简介,下载,安装 |---Groovy语言的简单认识 |---Gradle构建java项目 |---Gradle构建脚本的书写 |---Gradle构建java多模块项目 |---Gradle在Android中的应用 |---最后写一个创建文件夹的小插件
一、Gradle简介
1.我与Gradle的邂逅
百分之八十的Gradle使用者应该都是从
AndroidStudio
接触Gradle
的
想当年用Eclipse喜欢收藏jar包,但版本迭代后,还要重新找 虽然挺麻烦,但是有jar包用感觉也是无比幸福的,毕竟别人的心血能省自己不少事 毕竟简单的学习小项目不需要那么多第三方依赖,更不用说什么依赖管理 当我刚用AndroidStudio时,最不解的就是Gradle,开始一段时间基我俩井水不犯河水 我依然用着我的jar包,在src下写代码,似乎不用Gradle也没什么影响 第一次接触Gradle是看一片介绍oKHttp的文章,照着写一句,然后神奇的就OK了 这让我很惊讶,当看到源码时发现已经下载到本地了。心想:现在这么智能了? 当我发现断网情况下依然可以使用本地的库文件,jar包就被我彻底抛弃了 一直以来Gradle对我来说就是添加依赖,感觉熟悉又陌生 就像一个人一直帮你干活,你却对它除了工作之外一无所知,这显然不太好
2.构建工具
Ant: 长江后浪推前浪,前浪已经over了 |---编译、测试、打包 Maven:使用xml标记构建脚本 |---依赖管理、编译、测试、打包、发布 Gradle:使用Groovy语言构建脚本 |---依赖管理、编译、测试、打包、发布、灵活的脚本
3.Gradle是什么,怎么安装?
一个基于
Groovy语言
的开源项目自动化构建工具
如果你用过AndroidStudio,Gradle已经被你下好了,直接打开下面的路径
你可以将bin目录加入环境变量,下面的几点就不用看了,
3.1:确保jdk已安装
C:\Users\Administrator>java -version java version "10.0.1" 2018-04-17 Java(TM) SE Runtime Environment 18.3 (build 10.0.1+10) Java HotSpot(TM) 64-Bit Server VM 18.3 (build 10.0.1+10, mixed mode)
3.2:下载Gradle,地址:
将解压后的bin文件夹目录加到环境变量即可,
3.3:查看是否安装正确
C:\Users\Administrator>gradle -v Welcome to Gradle 5.2! Here are the highlights of this release: - Define sets of dependencies that work together with Java Platform plugin - New C++ plugins with dependency management built-in - New C++ project types for gradle init - Service injection into plugins and project extensions
二、Groovy语言
1.Groovy简介
基于java虚拟机的动态语言 面向对象/脚本,完全兼容java语法
2.创建一个gradle项目
3.修改Gradle配置的方法
注:目前2019-2-7日:
gradle-5.2
在Idea里Build失败,gradle-4.10.1
没问题
估计是Idea插件的版本未更新,PS(Android目前也是用的gradle-4.10.1
)
修改Gradle配置,出现下面的界面,Gradle插件就运行ok了
4.Java VS Groovy
---->[java]------------------ public class Version { private int major; private int minor; public Version(int major, int minor) { this.major = major; this.minor = minor; } public int getMajor() { return major; } public void setMajor(int major) { this.major = major; } public int getMinor() { return minor; } public void setMinor(int minor) { this.minor = minor; } } Version version = new Version(2, 1); System.out.println(version.getMajor()); //2 ---->[Groovy]------------------ class Version { private int major private int minor Version(int major, int minor) { this.major = major this.minor = minor } } def version = new Version(2, 1) println version.major //2
5.Groovy的特色
类及方法默认public,字段自动getter,setter,直接点号获取 最后一个表达式的值作为返回值 == 等用于equals(),assert语句,弱类型, 分号可选,扩号可选,字符串三种,闭包
def age = 24//推到类型 assert age < 25 //断言 println age//省略括号 def name = '张风捷特烈'//单引号字符串 def say = "我是${name}"//双引号插值字符串 def code = //三引号原样输出 ''' 我是${name} def age = 24//推到类型 assert age < 25 //断言 ''' println say println code //list def platform = ["java", "Android", "ios"]//定义集合 platform << 'Linux'//添加元素 println platform.class//class java.util.ArrayList println platform.size()//4 //map def release = [ 'v0.01': '2018-12-13', 'v0.02': '2018-12-14', 'v0.03': '2018-12-15'] release.'v0.04' = '2018-12-16'//添加 println release.getClass()//class java.util.LinkedHashMap println release.size()//4 //闭包 def update ={ v-> return v+1 } def updateFun(Closure closure,int version) { closure(version) } def newVersion = updateFun(update, 1) println newVersion//2
三.创建项目
1.新建项目
Idea会为我们自动生成项目结构
2.写一个方法
/** * 作者:张风捷特烈 * 时间:2019/2/7/007:8:58 * 邮箱:1981462002@qq.com * 说明:将字符大写方法 */ public class UpCase { public static String toUpCase(String str) { System.out.println(str); return str.toUpperCase(); } }
3.导出jar包
打jar包非常简单,点两下就行了,(其中字符集的问题
后面解决
,不影响jar包使用)
4.使用jar包
打了jar包就用用吧,虽然实际中已经很少用jar包依赖了,这里演示一下
新建一个App的module,将jar包导入,并依赖,然后就能正常使用了
四、关于Gradle构建脚本
在
gradle根目录\src\core-api\org\gradle\api\Project.java
是一个interface
它定义了一个项目类,而build.gradle
中即使用了项目对象的属性和方法
这两个类是Gradle的核心,其中定义了很多方法,可以在.gradle文件中随意调用
|---比如打印一下当前项目目录 ---->[org.gradle.api.Project#getProjectDir]------- /** * <p>The directory containing the project build file.</p> * * @return The project directory. Never returns null. */ File getProjectDir(); ---->[App/build.gradle]---------- println getProjectDir()//J:\Java\GradleTest\toly\App
1.解放双手task
1.1 :简单的创建文件夹任务
public interface Task extends Comparable<Task>, ExtensionAware
Task是一个接口,可以助你完成一些无聊的工作,这里以创建三个文件夹为例
def mkDir = {//创建文件夹的方法 path -> def dir = new File(path) if (!dir.exists()) { dir.mkdirs() } } task mkDirTask() {//自定义一个任务 def paths = [ 'src/main/java/com/toly1994/app/adapter', 'src/main/java/com/toly1994/app/activity', 'src/main/java/com/toly1994/app/fragment', ] doFirst { paths.forEach(mkDir) } }
如果上面的看懂了,这里用projectName替换一下项目名
这样就可以在任意项目里创建这三个文件夹了,
task mkDirTask() {//自定义一个任务 def projectName = project.name.toLowerCase()//获取工程目录 def paths = [ "src/main/java/com/toly1994/${projectName}/adapter", "src/main/java/com/toly1994/${projectName}/activity", "src/main/java/com/toly1994/${projectName}/fragment", ] doFirst { paths.forEach(mkDir) } }
1.2:Task之间的依赖dependsOn
也就是在执行之前,先执行被依赖的Task
task mkDirTaskWithUtils{ dependsOn 'mkDirTask'//依赖mkDirTask任务 def projectName = project.name.toLowerCase()//获取去工程目录 def paths = [ "src/main/java/com/toly1994/${projectName}/utils" ] doFirst { paths.forEach(mkDir) } }
2.构建的生命周期
//在构建项目前调用的钩子函数 gradle.beforeProject { project -> println "-------beforeProject-------" } //配置解析前回调 gradle.taskGraph.whenReady { graph -> println "-------whenReady-------" } gradle.buildFinished { result -> println "-------buildFinished-------" }
3.依赖管理
3.1.简介
关于implementation和compile的区别,这里简单说一下。可详见:
工件坐标:(group,name,version) 工件仓库:mavenCentral/jcenter 依赖的传递:若A-->B B-->C 则A-->C implementation(编译期)-----曾经为compile |--1.加快编译速度(本模块编译)。 |--2.对外隐藏不必要的接口。 runtime(运行期)、testCompile(测试编译期)、testRuntime(测试运行期)
//使用mavenCentral仓库 repositories { mavenCentral() } //依赖管理 dependencies { //测试编译时依赖 testCompile group: 'junit', name: 'junit', version: '4.12'
3.2:mavenCentral与依赖使用
maven仓库相比大家都知道吧,简单说一下怎么查看一个依赖(okhttp为例):仓库网址
可以看到okhttp的工件坐标(group,name,version),它确定了唯一的存在
//依赖管理 dependencies { //测试编译时依赖 testCompile group: 'junit', name: 'junit', version: '4.12' // https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp implementation group: 'com.squareup.okhttp3', name: 'okhttp', version: '3.13.1' }
//简写形式 implementation'组:名:版本号' -----是不是很亲切 implementation 'com.squareup.okhttp3:okhttp:3.13.1'
3.3:okhttp的使用
/** * 作者:张风捷特烈 * 时间:2019/2/8/008:10:27 * 邮箱:1981462002@qq.com * 说明:测试使用okhttp */ public class Api { public static void main(String[] args) { doGet("http://www.toly1994.com:8089/api/android/note"); } public static void doGet(String url) { //1.获取OkHttpClient对象 OkHttpClient okHttpClient = new OkHttpClient(); //2.获取Request对象 Request request = new Request.Builder().get().url(url).build(); //3.将Request封装为Call对象 Call call = okHttpClient.newCall(request); //4.执行Call call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { System.out.println(e); } @Override public void onResponse(Call call, Response response) throws IOException String str = response.body().string(); System.out.println(str); } }); } }
3.4:使用其他的仓库
前面找不到就找下一个
repositories { mavenCentral()//使用mavenCentral仓库 google()//使用google仓库 jcenter()//使用jcenter仓库 maven {//maven私服 url 'https://jitpack.io' } }
4:版本冲突
4.1:版本冲突简介
okhttp:3.13.1
依赖了okio:1.17.2
,如果项目中再依赖okio:2.2.2
就会版本冲突
默认情况下版本冲突时,Gradle会自动使用最高版本,所以我们并不怎么烦神
4.2:自己解决版本冲突
自动使用最高版本,大多数情况都适用,但你还是有自定义解决方案的机会的
首先,显示版本冲突在哪里
configurations.all{ resolutionStrategy{ failOnVersionConflict()//版本冲突时报错 } }
4.3:强制使用某版本
configurations.all { resolutionStrategy { // failOnVersionConflict()//版本冲突时报错 force 'com.squareup.okio:okio:1.17.2'//强制指定版本 } }
4.4:排除一个依赖中的依赖
implementation('com.squareup.okhttp3:okhttp:3.13.1'){ exclude group: 'com.squareup.okio',module:'okio' }
五、项目的模块化构建及测试
0.项目的模块化
1.settings.gradle
一个项目只有一个,用来管理子模块名称
rootProject.name = 'toly-all' include 'model' include 'repository' include 'player'
2.添加模块间依赖关系
implementation project(":模块名")
-或compile
注意:项目间的传递型依赖要用compile,不然引用不到
---->[toly-all\repository\build.gradle]------------ dependencies { compile project(":model") testCompile group: 'junit', name: 'junit', version: '4.12' } ---->[toly-all\player\build.gradle]------------ dependencies { testCompile group: 'junit', name: 'junit', version: '4.12' compile project(":repository") } ---->[toly-all\build.gradle]------------ dependencies { testCompile group: 'junit', name: 'junit', version: '4.12' implementation project(":player") }
3.Gradle信息的公共处理
每个
build.gradle
都有的东西,统一处理一下,以后改起来方便 在根项目下的build.gradle
里使用allprojects
---->[toly-all\build.gradle]------------ allprojects { apply plugin:'java' sourceCompatibility = 1.8 repositories { mavenCentral() } dependencies { testCompile group: 'junit', name: 'junit', version: '4.12' } }
4.使用gradle.properties
统一配置参数
---->[toly-all\gradle.properties]------------ group='com.toly1994' version='0.01'
5.测试
public class PlayerTest { @Test public void tolySay() { Player player = new Player(); Person person = player.tolySay(); assert person.name.equals("toly"); assert person.age == 24; } }
6.发布
使用maven-publish插件,发表到中央仓库挺麻烦的,还是自己搭个私服,或直接本地吧
以上是Gradle在java中的使用,现在回头看一下Android里的Gradle,你应该更有感觉
六、Gradle在Android中
1.现在新建一个Android普通项目
---->[模块:build.gradle]----------------- apply plugin: 'com.android.application' //启用插件 com.android.application android {//安卓 compileSdkVersion 27//SDK编译版本 defaultConfig {//默认配置 applicationId "com.toly1994.gradletest"//应用id minSdkVersion 21//兼容的SDK最低版本 targetSdkVersion 27//SDK目标版本(本应用的SDK--向下兼容) versionCode 1//版本号 versionName "1.0" //版本名称 testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes {//build配置 release {//发布设置 minifyEnabled false //是否混淆 //混淆文件 proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies {//依赖 implementation fileTree(include: ['*.jar'], dir: 'libs') implementation 'com.android.support:appcompat-v7:27.1.1' implementation 'com.android.support.constraint:constraint-layout:1.1.3' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' } ---->[项目:build.gradle]----------------- buildscript {//构建脚本 repositories { google() jcenter() } dependencies { classpath 'com.android.tools.build:gradle:3.2.1' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } } allprojects {//对所有模块适用 repositories {//仓库 google()//google仓库 jcenter()//jcenter仓库 } } task clean(type: Delete) {//清除的task delete rootProject.buildDir }
2.现在新建一个JNI的Android项目
模块级的gradle文件多了
externalNativeBuild
android { ... defaultConfig { ... externalNativeBuild { cmake { cppFlags "" } } } ... externalNativeBuild {//设置cmake的目录 cmake { path "CMakeLists.txt" } } }
将
stringFromJNI
抽取出来放在一个类中作为静态方法,然后生成so文件
3.回到前一个工程,使用so文件
注意so文件在其他工程下需要保证接口名的一致,比不刚才的C++中的函数:
Java_com_toly1994_jni_HelloJNI_stringFromJNI
该函数只能用在:com.toly1994.jni下的HelloJNI类中的stringFromJNI方法,错一个字都不行
android {//安卓 ... sourceSets {//------默认如下,可不用配置 main { jni.srcDirs = [] jniLibs.srcDirs = ['src/main/jniLibs']//默认路径,可修改 } } }
4.资源分包
这里以布局为例,其他资源文件夹也一样
android { ... sourceSets { main { res.srcDirs = [ 'src/main/res/layouts/home', 'src/main/res/layouts/player', 'src/main/res/layouts/news', 'src/main/res' ] }} }
public interface AndroidSourceSet
接口定义很多文件的位置
都可以根据自己的需要,自行修改
res.srcDirs 资源文件目录 assets.srcDirs assets文件目录 aidl.srcDirs aidl文件目录 jniLibs.srcDirs .so文件目录 jni.srcDirs jni文件目录 manifest.srcFile AndroidManifest.xml的位置 java.srcDirs java代码的文件目录
5.混淆与发布
提一下:
gradle.properties
里的键值对可以在.gradle里直接使用
你可以将密码写在里面,.gitignore
配置一下,不上传到github就行了
android { ... signingConfigs { release { storeFile file("tolyapp.jks") storePassword APK_SIGN_STORE_PASSWORD keyAlias "toly" keyPassword APK_SIGN_KEY_PASSWORD } } buildTypes { release { shrinkResources true//是否去除未利用的资源,默认false,表示不去除。 minifyEnabled true//是否混淆 signingConfig signingConfigs.release//签名 proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } ---->[gradle.properties]-------------- APK_SIGN_KEY_PASSWORD=777777 APK_SIGN_STORE_PASSWORD=666666
6.公共信息提取:ext
对于很多公共的东西,提取出来容易统一管理和修改,特别对于多模块项目而言
7.引用其他的.gradle文件
.gradle 文件一直被我认为是神圣的存在,不能乱改,更别提自己创建了
骑士gradle文件是可以相互引用的,下面通过创建文件夹小插件来说明
---->[mkdir.gradle]--------- import java.util.function.Consumer apply plugin: 'com.android.application' apply plugin: MkDirPlugin//声明使用插件 mkDir {//根据拓展参数来自定义文件夹 pkg = 'com.toly1994.gradletest_' names = ['adapter','activity','app/config', 'app/compat','utils','view','presenter','model'] } //----------------------------以下是插件部分-------------------------------- class MkDirPlugin implements Plugin<Project> { //该接口定义了一个apply()方法,在该方法中,我们可以操作Project, //比如向其中加入Task,定义额外的Property等。 void apply(Project project) { //加载Extension project.extensions.create("mkDir", MkDirPluginPluginExtension) def mkDir = { //创建文件夹的方法 path -> def dir = new File(path) if (!dir.exists()) { dir.mkdirs() } } //使用Extension配置信息 project.task('mkDirTask') << { String pkg = project.mkDir.pkg ArrayList<String> names = project.mkDir.names def dir='src/main/java/'+pkg.replaceAll("\\.",'''/''') ArrayList<String> paths = new ArrayList<>() names.forEach(new Consumer<String>() { @Override void accept(String s) { paths.add(dir+"/"+s) println dir+"/"+s } }) paths.forEach(mkDir) } } } class MkDirPluginPluginExtension {//拓展参数 String pkg = '' def names = [] } //----------------------------插件结束-------------------------------- ---->[模块级build.gradle]-------------- apply from: 'mkdir.gradle' //引用mkdir.gradle ---一行搞定
插件部分你不用Groovy,全部用java写都可以,Groovy对java是兼容的
插件你也可以新建一个项目来制作
,可以发布一下,给更多人使用 所以燃烧你的小宇宙,用gradle尽情偷懒吧!相信你会发现另一片天地!
后记:捷文规范
1.本文成长记录及勘误表
项目源码 | 日期 | 附录 |
---|---|---|
V0.1-- | 2018-2-12 | 无 |
发布名:
一代版本一代神[-Gradle-]
捷文链接:www.jianshu.com/p/075f84620…
2.更多关于我
笔名 | 微信 | |
---|---|---|
张风捷特烈 | 1981462002 | zdl1994328 |
我的github:github.com/toly1994328
我的简书:www.jianshu.com/u/e4e52c116…
我的掘金:juejin.im/user/5b42c0…
个人网站:www.toly1994.com
3.声明
1----本文由张风捷特烈原创,转载请注明
2----欢迎广大编程爱好者共同交流
3----个人能力有限,如有不正之处欢迎大家批评指证,必定虚心改正
4----看到这里,我在此感谢你的喜欢与支持
作者:张风捷特烈