Android之Gradle以及Groovy语法
一.groovy语法
1.注释符
与java一致
2.打印 , 字符串,单引号和双引号没有区别
groovy可以省略最后面的分号,以及()
println "Hello from the shebang line"
3.定义变量和全局遍历
def i = 10 //相当于java 本地变量
println i
def s = 'hello world'
println s
ext { //相当于static
gradleName = "gradleName"
}
task hello() {
println "${gradleName}"
}
4.定义集合List集合和map集合
List集合
def list = ['a','b'] //定义集合
list << 'c' //添加元素
println list[2] //打印
println list.get(2) //等价
Map集合
def map =['key1':'value1','key2':'value2'] //定义map集合
map.key3 = 'value3' //如果存在赋值,如果不存在,添加
map.key1 = 'v1' //赋值
println map['key1'] //打印
println map.get('key1') //等价
5.闭包
闭包原理:闭包实际是一个代码块,函数当作参数使用,也就是将函数当作对象来看,也有属性
函数参数用Closure使用
函数不带有参数
def b1 = {
println 'hello'
}
def method1(Closure fun) {
fun()
}
method1(b1)
带有参数的闭包
def b2 = { //只有一个参数时,可以用it代替,如同kotlin
println it
}
def b3 = {
a,c -> println "${a} ${c}"
}
def method2(Closure fun) {
fun('a','b')
}
method2(b3)
也可以使用简写,匿名函数
method2 {
c,d ->
println "${c}${d}"
}
6.Task任务
task gradleWrapp(type:Wrapper) {
gradleVersion = '2.0'
println 'xxx'
}
7任务的拆分与聚合
8 switch
println "------------------switch--------------------"
def x = "a";
def result;
switch (x.class) { //这个相当于
case "a":
result = "我是 a"
break
case 'b':
result = "我是 b"
break
case [4, 5, 6, 'c']://列表
result = "我是个数组"
break
case 10..50://范围
result = "我是10到50的数"
break
case Integer:
result = "我是Integer类型"
break
case BigDecimal:
result = "我是BigDecimal类型"
break
case String:
result = "我是String类型"
break
default:
result = "default"
break
}
9 List for循环
def sum1=0;
task gradleWrapp() {
for (int i in 1..5) {
println i
}
for (i in [1,3,5,32,4,2,6,9,5,45,1]){
sum1+=i;
}
println sum1//1
}
10 map循环
def sum2=0;
task gradleWrapp() {
for (i in ['a':1,"b":2,"c":"ha"]){
sum2+=i.value
}
println sum2//3ha
}
11.数据结构
def range=1..10;
task rangeWrap() {
println range[0]//1
println range.contains(10)//true
println range.contains(11)//false
println range.from//1
println range.to//10
}
12 List遍历
def list = ['a', 'b'] //定义集合
list << 'c' //添加元素
println list[2] //打印
println list.get(2) //等价
list.each {
println it
}
13.Map遍历
def map = ['key1': 'value1', 'key2': 'value2'] //定义map集合
map.key3 = 'value3' //如果存在赋值,如果不存在,添加
map.key1 = 'v1' //赋值
map.each {
println it.value //获取value值
}
key和value都打印
def map = ['key1': 'value1', 'key2': 'value2'] //定义map集合
map.key3 = 'value3' //如果存在赋值,如果不存在,添加
map.key1 = 'v1' //赋值
map.each {
key,value->println "${key}${value}"
}
14 String操作符 find
String str = "love and 2 to be 1 love all 8 happiness"
println str.find {
String s -> s.isNumber()
} //2
println str.any {
s -> s.isNumber()
}
/**
* 要全部满足才返回true
*/
println str.every {
s -> s.isNumber()
}
15定义内部类
/**
* 定义了一个内部类
*/
class Person{
def static classClouser={
println "scriptClouser this:"+this
println "scriptClouser owner:"+owner
println "scriptClouser delegate:"+delegate
}
def static say(){
def classClouser={
println "methodScriptClouser this:"+this
println "methodScriptClouser owner:"+owner
println "methodScriptClouser delegate:"+delegate
}
classClouser.call()
}
}
16创建一个对象
class Student{
String name;
def pretty ={
"My name is ${name}"
}
String toString(){
pretty.call()
}
}
class Teacher{
String name
}
def stu = new Student(name:"Student")
def tea = new Teacher(name: 'Teacher')
println stu.toString()//My name is Student
17委托
stu.pretty.delegate=tea
//修改委托策略(DELEGATE_FIRST,DELEGATE_ONLY......)
stu.pretty.resolveStrategy=Closure.DELEGATE_FIRST//有限寻找tea,找不到再找stu
println stu.toString()//My name is Teacher
18面向对象
1.创建接口
/**
* 接口中不允许定义非public的方法
*/
interface Action {
void eat()
void drink()
void play()
}
2.创建抽象类
/**
* 类似于interface ,但是trait可以进行方法的实现(类似java中的abstract)
*/
trait DefaultAction {
abstract void eat();
void play1(){
println 'DefaultAction'
}
}
3.对象
def p2 = new Persion2(name: 'android', age: 26)
println p2.name
/**
* 1.groovy中默认都是public类型
*/
class Persion2 implements Action, DefaultAction {
String name
Integer age
def increaseAge(Integer years) {
this.age = years
}
@Override
void eat() {
}
@Override
void drink() {
}
@Override
void play() {
}
/**
* 一个方法找不到时,调用它代替
* @param name
* @param args
* @return
*/
def invokeMethod(String name, Object args) {
return "name=>${name},args=>${args}"
}
def methodMissing(String name, Object args){
return "the method ${name} is missing"
}
}
19json操作
import groovy.json.JsonOutput
import groovy.json.JsonSlurper
import objectorention.Persion
def list=[new Persion(name: 'huangxiaoguo1',age: 25),
new Persion(name: 'huangxiaoguo2',age: 26),
new Persion(name: 'huangxiaoguo3',age: 27),
new Persion(name: 'huangxiaoguo4',age: 28)]
/**
* 将对象转换为json
*/
println JsonOutput.toJson(list)
/**
* 打印格式化的json串
*/
def json = JsonOutput.toJson(list)
println JsonOutput.prettyPrint(json)
/**
* 将json串转换为实体对象
*/
def jsonSlpuer=new JsonSlurper()
def parse = jsonSlpuer.parseText(json)
println parse.toString()
println "--------------------------------groovy中进行网络请求---------------------------------------------"
def getNetworkData(String url){
//发送http请求
def connection=new URL(url).openConnection()
connection.setRequestMethod('GET')
connection.connect()
def request = connection.content.text
//将json转换为实体对象
def jsonSlpuer=new JsonSlurper()
return jsonSlpuer.parseText(request)
}
def reponse = getNetworkData("http://******/wsd/dictionary/getMapByDicflag?dicFlag=wsd.grade.type")
println reponse.data
20File操作
/**
* 文件操作
*/
def file = new File('../../groovy.iml')
file.eachLine { line ->
println line
}
println "-----------------------------------------------------------------------------------------"
def text = file.getText()
println text
println "-----------------------------------------------------------------------------------------"
def lines = file.readLines()
println lines
println "-----------------------------------------------------------------------------------------"
/**
* 读取文件部分内容
*/
def reader = file.withReader { reader ->
char[] buffer = new char[100]
reader.read(buffer)
return buffer
}
println reader
println "-----------------------------------------------------------------------------------------"
/**
* 边读边写
* @param sourcePath
* @param desPath
* @return
*/
def copy(String sourcePath, String desPath) {
try {
//创建目标文件
def desFile = new File(desPath)
if (!desFile.exists()) {
desFile.createNewFile()
}
//copy
new File(sourcePath).withReader { reade ->
def lines = reade.readLines()
desFile.withWriter { writer ->
lines.each { line ->
writer.append(line + "\r\n")
}
}
}
return true
} catch (Exception e) {
e.printStackTrace()
}
return false
}
def result = copy("../../groovy.iml", "../../groovy2.iml")
println result
println "-----------------------------------------------------------------------------------------"
/**
* 对象的读写
*/
def person = new Person(name: "huangxiaoguo",age: 18)
def saveObject(Object obj, String path) {
try {
//创建文件
def desFile = new File(path)
if (!desFile.exists()) {
desFile.createNewFile()
}
desFile.withObjectOutputStream { out ->
out.writeObject(obj)
}
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
}
//从文件中读取对象
file.withObjectInputStream { input ->
obj = input.readObject()
}
} catch (Exception e) {
e.printStackTrace()
}
return obj
}
println saveObject(person,"../../person.txt")
def personr = (Person)readObject("../../person.txt")
println "name:${personr.name},age:${personr.age}"
21.注意一种操作,闭包自执行
/**
* 闭包调用
*/
task hello2 {
doLast {
println "helloxxxxxx"
}
}
//这种也会直接调用,省略了()
task getServer {
println getSERVER2('release')
println getSERVER2('debug')
}
二、Android中gradle解答
//表示先从maven {url} 中下载,如果有,则下载,没有,在google()中查找下载,链式关系
repositories {
maven{ url 'http://maven.aliyun.com/nexus/content/groups/public/'}
google()
jcenter()
maven { url 'https://jitpack.io' }
}
1.了解Android中build.gradle文件
buildscript {
repositories {
//依赖支持jcenter仓库获取
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.1.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
//当前的project的所有子模块,即settings.gradle中所有项目
subprojects{
//共有配置
}
//当前project中所有项目均
allprojects {
repositories {
jcenter()
}
}
//clean 任务可以帮助我们删除编译生成的build目录
task clean(type: Delete) {
delete rootProject.buildDir
}
2.常见配置
//插件声明,这个也可以自定义
apply plugin: 'com.android.application'
//基本的android 配置
android {
compileSdkVersion 23 //编译的sdk版本,debug
buildToolsVersion "23.0.3" //编译工具的版本
//android的默认配置信息
defaultConfig {
//报名信息,可以没有,若没有会默认从manifest.xml中读取包名
applicationId "com.hfcai.liferecord"
minSdkVersion 19
targetSdkVersion 23 //正是版本,release
versionCode 1
versionName "1.0"
}
buildTypes {
//发布版
release {
//dex分包属性
minifyEnabled false
//混淆设置
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
//测试版
debug{
//配置信息
}
}
//apk签名配置
signingConfigs {
//测试版本
release {
//签名文件路径
storeFile file('../keystore/personal.jks')
//签名文件的密码
storePassword 'personal'
//用户名
keyAlias 'personal'
//用户名的密码
keyPassword 'personal'
}
}
//修改生成的apk名字
applicationVariants.all { variant ->
variant.outputs.each { output ->
//定义无类型属性
def oldFile = output.outputFile
if (variant.buildType.name.equals('release')) {
def releaseApkName = 'test.apk'
output.outputFile = new File(oldFile.parent, releaseApkName)
}
if (variant.buildType.name.equals('debug')) {
}
output.assemble.doLast {
//文件拷贝操作
copy {
from output.outputFile.getAbsolutePath()
into('../Personal/out/apks/')
}
}
}
}
}
//android 的依赖管理配置
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:23.4.0'
compile 'com.android.support:design:23.4.0'
//Ivy依赖
compile(name: 'AudienceNetwork', ext: 'aar')
}
3.解决debug版本与release版本网络地址配置问题
1.第一步,本地生成两个Properties文件(realse和debug),用于保存网络地址
为什么选取Properties文件而不是File文件,了解Properties特性,适合做配置文件,获取 =后面的值
debug
SERVER2 = "https://www.youku.com"
realse
SERVER2 = "https://www.baidu.com"
2.根据debug和release来分别获取网络地址
/**
* 获取服务地址
* @param str
* @return
*/
def getSERVER2(String str) {
def SERVER2
Properties properties = new Properties()
def proFile = file("src/main/filters/" + str + "/config.properties")
if (proFile.canRead()) {
try {
InputStream is = new FileInputStream(proFile)
properties.load(is)
if (properties != null) {
SERVER2 = properties['SERVER2']
}
is.close()
} catch (Exception e) {
e.printStackTrace()
}
}
return SERVER2
}
3.需要将网络的参数,写道BuildConfig中,那么如何写进去了,通过buildConfigField配置参数
buildTypes: 编译时,确定是哪个版本debug,release或者更多
buildTypes {
debug {
//第一个参数时传声明类型对象,第二个参数需要传名字,第三个参数就是属性的值
buildConfigField 'String', 'SERVER2', getSERVER2('debug')
}
release {
buildConfigField 'String', 'SERVER2', getSERVER2('release')
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
java中调用
private void postServer() {
String server = BuildConfig.SERVER2;
Log.d("MainActivityxxx", server);
}
4.多渠道打包并重新给apk命名
给apk添加,年月日时分秒以及版本文件名,方便测试去测试以及上架
1.添加flavorDimensions “versionCode”
defaultConfig {
applicationId "com.uih.gradledemo"
minSdkVersion 28
targetSdkVersion 30
versionCode 1
versionName "1.0"
flavorDimensions "versionCode"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
2.添加多渠道打包
productFlavors {
google {
buildConfigField 'String', 'PLAT_FORM', "\"google\""
manifestPlaceholders = [
SDKChannel: "google",
app_name : "@string/app_name"
]
}
baidu {
buildConfigField 'String', 'PLAT_FORM', "\"baidu\""
manifestPlaceholders = [
SDKChannel: "baidu",
app_name : "@string/app_name"
]
}
xiaomi {
buildConfigField 'String', 'PLAT_FORM', "\"xiaomi\""
manifestPlaceholders = [
UMENG_CHANNEL_VALUE: "xiaomi",
app_name : "@string/app_name"
]
}
}
3.给apk重新命名
android.applicationVariants.all { variant ->
variant.outputs.all {
println "SDK_${variant.flavorName}_${variant.buildType.name}_V${variant.versionName}_${buildTime()}.apk"
//在这里修改apk文件名,引号内的字符串都可以随便定义
outputFileName = "SDK_${variant.flavorName}_${variant.buildType.name}_V${variant.versionName}_${buildTime()}.apk"
}
}
/**
* 时间
* @return
*/
def buildTime() {
def date = new Date()
def formattedDate = date.format('yyyyMMddHHmmss')
return formattedDate
}
说明:
-
{variant.flavorName}
:渠道名称; -
{variant.buildType.name}
:release或debug包; -
{variant.versionName}
:版本名称; 等价于defaultConfig.versionName -
{buildTime()}
:当前时间;
5.自动签名
1.添加flavorDimensions
defaultConfig {
applicationId "com.uih.gradledemo"
minSdkVersion 28
targetSdkVersion 30
versionCode 1
versionName "1.0"
flavorDimensions "versionCode"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
2.buildType中确定使用签名 signingConfig signingConfigs.release
buildTypes {
debug {
//第一个参数时传声明类型对象,第二个参数需要传名字,第三个参数就是属性的值
buildConfigField 'String', 'SERVER2', getSERVER2('debug')
}
release {
buildConfigField 'String', 'SERVER2', getSERVER2('release')
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
}
6.如何统计下载量
7.安全打包
我们的应用发布的时候需要使用一个keyStroe签名文件和签名密码,对于一个app来说,这个就是app的身份证,如果别人拿到了这些资料就可以重新打包应用,为了安全,我们应该把线上包的签名和密码与代码分开来保存。现在的一般做法就是把这些资源写在配置文件,再通过脚本来修改打包。
我们一般都使用这两个文件来保存密码,使用的方式有点不同。
第一种方式,在gradle.properties文件,直接在文件内定义变量即可
signname=key0
signPassword=123456
——————注意:key0和123456不要加双引号,否则在gradle.properties中,会当作是“key0”,
//签名
signingConfigs{
release{
storeFile file("E:\\TestJks\\test.jks")
storePassword signPassword.toString()
keyAlias signname.toString()
keyPassword signPassword.toString()
}
}
整体代码
build.gradle
apply plugin: 'com.android.application'
android {
compileSdkVersion 30
buildToolsVersion "30.0.1"
defaultConfig {
applicationId "com.uih.gradledemo"
minSdkVersion 28
targetSdkVersion 30
versionCode 1
versionName "1.0"
flavorDimensions "versionCode"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
//签名
signingConfigs {
release {
storeFile file("E:\\TestJks\\test.jks")
storePassword signPassword
keyAlias signname
keyPassword signPassword
}
}
buildTypes {
debug {
//第一个参数时传声明类型对象,第二个参数需要传名字,第三个参数就是属性的值
buildConfigField 'String', 'SERVER2', getSERVER2('debug')
}
release {
buildConfigField 'String', 'SERVER2', getSERVER2('release')
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
}
productFlavors {
google {
buildConfigField 'String', 'PLAT_FORM', "\"google\""
manifestPlaceholders = [
UMENG_CHANNEL_VALUE: "google",
app_name : "@string/app_name"
]
}
baidu {
buildConfigField 'String', 'PLAT_FORM', "\"baidu\""
manifestPlaceholders = [
UMENG_CHANNEL_VALUE: "baidu",
app_name : "@string/app_name"
]
}
xiaomi {
buildConfigField 'String', 'PLAT_FORM', "\"xiaomi\""
manifestPlaceholders = [
UMENG_CHANNEL_VALUE: "xiaomi",
app_name : "@string/app_name"
]
}
}
android.applicationVariants.all { variant ->
variant.outputs.all {
println "SDK_${variant.flavorName}_${variant.buildType.name}_V${variant.versionName}_${buildTime()}.apk"
//在这里修改apk文件名,引号内的字符串都可以随便定义
outputFileName = "SDK_${variant.flavorName}_${variant.buildType.name}_V${variant.versionName}_${buildTime()}.apk"
}
}
}
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}
task getServer {
println getSERVER2('release')
println getSERVER2('debug')
}
/**
* 获取服务地址
* @param str
* @return
*/
def getSERVER2(String str) {
def SERVER2
Properties properties = new Properties()
def proFile = file("src/main/filters/" + str + "/config.properties")
if (proFile.canRead()) {
try {
InputStream is = new FileInputStream(proFile)
properties.load(is)
if (properties != null) {
SERVER2 = properties['SERVER2']
}
is.close()
} catch (Exception e) {
e.printStackTrace()
}
}
return SERVER2
}
/**
* 时间
* @return
*/
def buildTime() {
def date = new Date()
def formattedDate = date.format('yyyyMMddHHmmss')
return formattedDate
}
gradle.properties
signname=key0
signPassword=123456
AndroidManifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.uih.gradledemo">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<meta-data
android:name="UMENG_CHANNEL"
android:value="${UMENG_CHANNEL_VALUE}" />
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
参考:https://blog.csdn.net/seemygoandroid?t=1
————————Gradle从入门到精通