Android Gradle学习记录6 代码记录(持续补充)
本篇博客主要记录一些,自己觉得比较有价值的Gradle代码片段。
1、生成doc文档:
ext {
sdkDocOption = ['encoding' : 'utf-8',
'locale' : 'en_US',
'docEncoding' : 'utf-8',
'charSet' : 'utf-8',
'header' : 'AVLSDK API',
'footer' : 'AVLSDK API',
'windowTitle' : 'AVLSDK API',
'title' : 'AVLSDK API',
'include' : 'com/avl/engine/*.java',
'destinationDir' : 'sdk/doc']
}
//继承Javadoc类型的Task
//为source对应的文件生成Java Doc
task buildSdkDoc(type: Javadoc, dependsOn: 'copyClasses') {
source = android.sourceSets.main.java.srcDirs
classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
options.encoding = sdkDocOption.get('encoding')
options.locale = sdkDocOption.get('locale')
options.docEncoding = sdkDocOption.get('docEncoding')
options.charSet = sdkDocOption.get('charSet')
options.header = sdkDocOption.get('header')
options.footer = sdkDocOption.get('footer')
options.windowTitle = sdkDocOption.get('windowTitle')
title = sdkDocOption.get('title')
include(sdkDocOption.get('include'))
destinationDir = reporting.file(sdkDocOption.get('destinationDir'))
failOnError false
}
2、生成Jar包并混淆
ext {
//Decide class files that will not be added into jar
excludeFilesWhenBuildJar = [
'com/example/**/BuildConfig.class',
'com/example/**/R.class',
'com/example/**/R$*.class'] as String[]
//proguard file
configurationFile = [file('proguard.txt'),] as File[]
needPackageObfuscation = false
sdkJarName = 'ExampleSDK.jar'
proguardSdkJarName = 'ExampleSDK-proguard.jar'
}
//继承Jar类型的Task
//将build/sdk/classes下的文件,打包成Jar
//除去excludeFilesWhenBuildJar中的文件
task buildSdkJar(type: Jar, dependsOn: ['copyClasses', 'buildSdkDoc'], description: "build sdk jar") {
from 'build/sdk/classes'
for (String fileName : excludeFilesWhenBuildJar) {
exclude(fileName)
}
includeEmptyDirs false
archiveName sdkJarName
}
//继承ProGuardTask类型的Task
//根据混淆文件,混淆Jar包
task buildProGuardJar(type: ProGuardTask, dependsOn: buildSdkJar) {
doFirst {
def proguardDir = file("$buildDir/libs/proguard")
proguardDir.mkdirs()
}
//未混淆的Jar包
injars file("${buildDir}/libs/${sdkJarName}")
//混淆后Jar的存储地址
outjars file("${buildDir}/libs/${proguardSdkJarName}")
//指定其它需要文件的地址
libraryjars android.getBootClasspath() + ";" + file("libs/android-support-v4.jar")
printseeds file("${buildDir}/libs/proguard/seed.txt")
printusage file("${buildDir}/libs/proguard/usage.txt")
printmapping file("${buildDir}/libs/proguard/mapping.txt")
dump file("${buildDir}/libs/proguard/dump.txt")
//指定混淆需要的配置文件
//可以指定多个文件
configuration file(rootProject.getRootDir().getAbsolutePath() + '/SDKConfig/proguard-base.txt')
configuration configurationFile
if (needPackageObfuscation) {
//指定package的模糊字典
packageobfuscationdictionary file("packageobfuscationdictionary.txt")
}
//NOTE:proguard has bug,dump file not generated in specified folder
doLast {
copy {
from 'build/libs/dump.txt'
into 'build/libs/proguard'
}
}
}
3、copy闭包
普通的Task,即使不继承CopyTask,也可以使用copy闭包。
def copySdkDoc
task test() {
doLast {
copySdkDoc()
}
}
copySdkDoc = {
........
//定义好目的地址
def userDir = new File(user_path)
if (userDir.exists()) {
delete userDir
}
userDir.mkdirs()
copy {
//指定源文件地址
from("$projectDir/build/reports/sdk") {
//指定包含的文件类型
include('**/*')
}
into(user_path)
}
}
copy的同时,可以重命名文件:
copyJarAndRepackage = {
def sdkVer = getSdkVersionFromXml();
copy {
from("$projectDir/build/libs")
into(user_path)
//sdkJarName和proguardSdkJarName
//均是"$projectDir/build/libs"下的文件名
rename(sdkJarName, "${outputName}_unproguard.jar")
rename(proguardSdkJarName, "${outputName}_" + sdkVer + '.jar')
}
}
4、解压zip包
//File表示源文件
//newPath对应解压后的存储路径
def static void unzipFile(File file, String newPath) {
def zipFile = new ZipFile(file)
def entries = zipFile.entries()
while (entries.hasMoreElements()) {
def entry = (ZipEntry) entries.nextElement()
def zipEntryName = entry.getName()
//必要是将路径中的"\"替换为"/"
def outPath = (newPath + File.separator + zipEntryName).replaceAll("\\\\", "/")
def outDir = new File(outPath.substring(0, outPath.lastIndexOf('/')))
if (!outDir.exists()) {
outDir.mkdirs()
}
if (new File(outPath).isDirectory()) {
continue
}
def input = zipFile.getInputStream(entry)
def output = new FileOutputStream(outPath)
def buf = new byte[1024]
def len
while ((len = input.read(buf)) > 0) {
output.write(buf, 0, len)
}
input.close()
output.close()
}
}
5、计算文件的md5
def static String generateMd5BaseFile(File file) {
def md5 = MessageDigest.getInstance("MD5")
def buffer = new byte[1024]
def numRead
def fis = new FileInputStream(file)
while ((numRead = fis.read(buffer)) > 0) {
md5.update(buffer, 0, numRead)
}
def HEX_DIGITS = ['0', '1', '2', '3', '4', '5',
'6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F']
def bArray = md5.digest()
def sb = new StringBuilder(bArray.length * 2)
for (byte b : bArray) {
sb.append(HEX_DIGITS[(b & 0xf0) >>> 4])
sb.append(HEX_DIGITS[b & 0x0f])
}
fis.close()
return sb.toString()
}
6、解析Json字符串
//info对应json字符串
//key为需查找的key
def String getValueFromJsonString(String key, String info) {
def parsedJson = new groovy.json.JsonSlurper().parseText(info)
return parsedJson.get(key)
}
参考:
http://groovys.readthedocs.io/zh/latest/Groovy-module-guides/Parsing-and-producing-JSON.html
http://docs.groovy-lang.org/latest/html/gapi/groovy/json/JsonBuilder.html#call(java.util.Map)
7、使用Jar包
在Gradle中,通过命令使用Jar包时,需要注意这是一个异步操作,
即命令将在单独的线程中执行。
def void encryptBaseConfAseJar(String srcFilePath, String outFilePath, String flagPath) {
..............
//这种方式是靠控制台命令来执行jar包的
//指定jar路径,并提供参数即可
def cmd = 'java -jar ' + jarPath + ' -encrypt '+
srcFilePath + ' ' + outFilePath + ' ' + keyDir + ' ' + flagPath
cmd.execute()
}
8、正则表达式
def static String getVersionBasePattern(String path) {
def file = new File(path)
def input = new FileInputStream(file)
def bytes = new byte[input.available()]
input.read(bytes)
input.close()
def info = new String(bytes, "GB2312")
def p = Pattern.compile("Build \\d+")
def m = p.matcher(info)
def target = []
while (m.find()) {
target.add(m.group())
}
p = Pattern.compile("[^0-9]")
def ret
for (String str : target) {
m = p.matcher(str)
ret = m.replaceAll('').trim()
if (ret != '') {
return ret
}
}
return ''
}
9、AES加解密
加密过程如下:
//srcFilePath源文件地址
//destFilePath加密后的文件地址
def void aesEncrypt(String srcFilePath, String destFilePath) {
//16进制的AES_KEY
def AES_KEY = ".........."
def ALGORITHM = "AES"
def TRANSFORMATION = "AES/ECB/PKCS5Padding"
def sKeySpec = new SecretKeySpec(parseHexStr2Byte(AES_KEY), ALGORITHM)
def encryptCipher = Cipher.getInstance(TRANSFORMATION)
encryptCipher.init(Cipher.ENCRYPT_MODE, sKeySpec)
FileInputStream inStream = new FileInputStream(new File(srcFilePath))
CipherOutputStream cipherOutputStream =
new CipherOutputStream(new FileOutputStream(new File(destFilePath)), encryptCipher)
def read
while ((read = inStream.read()) != -1) {
cipherOutputStream.write((byte) read)
cipherOutputStream.flush()
}
inStream.close();
cipherOutputStream.close();
}
//将16进程变为byte数组
def static byte[] parseHexStr2Byte(String hexStr) {
if (hexStr.length() < 1) {
return null
}
def len = hexStr.length() / 2
def result = new byte[len]
for (int i = 0; i < len; i++) {
def high = Integer.parseInt(hexStr.substring(i << 1, (i << 1) + 1), 16)
def low = Integer.parseInt(hexStr.substring((i << 1) + 1, (i << 1) + 2), 16)
result[i] = (byte) (high * 16 + low)
}
return result;
}
解密过程如下:
def static String decrypt(byte[] rawByte) {
//加(解)密时使用的key
def KEY = [.......] as byte[]
//处理KEY
byte[] key = messKey(KEY)
SecretKeySpec sKey = new SecretKeySpec(key, "AES")
//加(解)密时使用的AES
def AES = [.......] as byte[]
Cipher cipher = Cipher.getInstance(new String(messKey(AES)))
cipher.init(Cipher.DECRYPT_MODE, sKey)
return new String(cipher.doFinal(rawByte))
}
def static byte[] messKey(byte[] bArray) {
def ret = new byte[bArray.length]
//定义key,作用类似于混淆,转换原始的key
byte key = ....
for (int i = 0; i < bArray.length; ++i) {
ret[i] = (byte) (bArray[i] ^ key)
}
return ret
}
10、Zip压缩
//inputPath源文件路径
//outPath目的文件路径
def void zipFile(String inputPath, String outPath) {
def inputFile = new File(inputPath)
if (!inputFile.exists()) {
println 'inputFile not exist: ' + inputPath
}
def outFile = new File(outPath)
if (outFile.exists()) {
delete outFile
}
outFile.createNewFile()
ZipOutputStream out = new ZipOutputStream(new FileOutputStream(outFile))
zipUtil(out, inputFile, '')
out.close()
}
//递归
def void zipUtil(ZipOutputStream out, File f, String baseName) {
if (f.name.startsWith('.')) {
return
}
if (f.isDirectory()) {
def fl = f.listFiles()
if (fl.length == 0) {
out.putNextEntry(new ZipEntry(baseName + "/"))
out.closeEntry()
return
}
def pre = baseName
if (baseName != '') {
pre = baseName + "/"
}
for (File file : fl) {
zipUtil(out, file, pre + file.name)
}
} else {
out.putNextEntry(new ZipEntry(baseName))
BufferedInputStream bi = new BufferedInputStream(new FileInputStream(f))
int num
while ((num = bi.read()) != -1) {
out.write(num)
}
bi.close()
out.closeEntry()
}
}
11、向Zip包中添加文件
//srcFilePath代表待添加文件的路径
//targetPath代表Zip包的路径
//dirName为Zip中的目录,srcFilePath对应的文件将被加入到dirName下
def void zipAdd(String[] srcFilePath, String targetPath, String dirName) {
def destFile = new File(targetPath)
if (!destFile.exists()) {
println 'can not find file: ' + targetPath
return
}
def targetFile = []
for (String src : srcFilePath) {
def file = new File(src)
if (file.exists()) {
targetFile.add(file)
}
}
zipAddUtil(targetFile, destFile, dirName)
}
def void zipAddUtil(ArrayList<File> srcFiles, File destFile, String dirName) {
def tempFile = File.createTempFile(destFile.getName(), null, destFile.getParentFile())
if (tempFile.exists()) {
delete tempFile
}
destFile.renameTo(tempFile)
byte[] buf = new byte[1024]
def zin = new ZipInputStream(new FileInputStream(tempFile))
def out = new ZipOutputStream(new FileOutputStream(destFile))
//First copy old files
def entry = zin.getNextEntry()
def name
def len
while (entry != null) {
name = entry.getName()
out.putNextEntry(new ZipEntry(name))
while ((len = zin.read(buf)) > 0) {
out.write(buf, 0, len)
}
out.closeEntry()
entry = zin.getNextEntry()
}
zin.close()
//Then insert new files
for (File srcFile : srcFiles) {
InputStream input = new FileInputStream(srcFile)
out.putNextEntry(new ZipEntry(dirName + "/" + srcFile.name))
while ((len = input.read(buf)) > 0) {
out.write(buf, 0, len)
}
out.closeEntry()
input.close()
}
out.close()
delete tempFile
}
12、获取当前的软件版本和TAG
def static int getGitVersionCode(String projectPath) {
//其实就是调用控制台命令
def cmd = 'git -C ' + projectPath + ' rev-list HEAD --first-parent --count'
def versionCodeStr = cmd.execute().text.trim()
if ("" == versionCodeStr) {
return 1
}
return versionCodeStr.toInteger()
}
def static String getGitVersionTag(String projectPath) {
def cmd = 'git -C ' + projectPath + ' describe --tags'
def version = cmd.execute().text.trim()
def pattern = "-(\\d+)-g"
def matcher = (version =~ pattern)
if (matcher) {
version = version.substring(0, matcher.start())
}
return version
}
13、代码检查
Gradle中可以直接通过集成的方式,定义代码检查需要的task。
例如:
lint:
android {
//android中直接支持lint检查
lintOptions {
abortOnError false
//定义输出格式为xml
xmlReport true
htmlReport false
//定义lint的检测规则
lintConfig file("...../lint.xml")
//定义lint结果输出路径
xmlOutput file(".../lint_result.xml")
}
}
checkStyle:
//为了进行代码检查,需要导入对应的插件
apply plugin: 'checkstyle'
task checkstyle(type: Checkstyle) {
ignoreFailures = true
//定义checkStyle的配置文件
configFile file("..../checkstyle.xml")
//定义checkStyle的忽略配置
//可以指定某些文件,忽略特定的checkStyle规则
configProperties.checkstyleSuppressionsPath = file("......./suppressions.xml").absolutePath
source = [android.sourceSets.main.java.srcDirs, 'src']
include '**/*.java'
exclude '**/gen/**', '**/test/**'
classpath = files()
reports {
//只能开启一个
xml.enabled = true
html.enabled = false
xml.destination "..../checkStyle_result.xml"
}
}
checkStyle的忽略文件类似于:
<suppressions>
//R.java中的变量,不需要满足仅由小写字符、大写字符和数字构成
<suppress checks="[a-zA-Z0-9]*" files="R.java" />
..........
<suppressions>
findbugs:
apply plugin: "findbugs"
task findBugs(type: FindBugs) {
ignoreFailures = true
effort = "max"
//findBugs可以指定报告的输出等级
reportLevel = "low"
//这里填写项目classes目录
classes = files("........./classes")
source = [android.sourceSets.main.java.srcDirs, 'src']
classpath = files()
includeFilter = file("......../findbugs-infilter.xml")
reports {
//只能开启一个
xml.enabled = true
html.enabled = false
xml.destination "..../findBugs_result.xml"
}
}
pmd:
apply plugin: 'pmd'
task pmd(type: Pmd) {
description 'Run pmd'
group 'verification'
ignoreFailures = true
ruleSetFiles = files("......./pmd-ruleset.xml")
ruleSets = []
source = [android.sourceSets.main.java.srcDirs, 'src']
include '**/*.java'
exclude '**/gen/**', '**/test/**'
reports {
xml.enabled = true
html.enabled = false
xml.destination "...../pmd_result.xml"
}
}
14、defaultTasks
Gradle允许在脚本中定义一个或多个默认任务,如下:
defaultTasks 'clean', 'buildSdk'
task clean << {
println 'Default Cleaning!'
}
task run << {
println 'Default build sdk!'
}
task other << {
println "I'm not a default task!"
}
此时直接调用gradle命令,不指定task,执行结果类似于:
Default Cleaning!
Default build sdk!
需要注意的是:
必须要在定义defaultTasks的gradle文件对应Project的目录下,执行gradle命令。
defaultTasks可以为当前Project或子Project定义的task。
举例来说就是:
假设有个Project A,对应的gradle文件为buildA.gradle;
Project A有个子Project B,对应的gradle文件为buildB.gradle。
如果在buildA.gradle中定义defaultTasks,那么task可以定义在buildA.gradle、buildB.gradle中。
但执行gradle时,必须在Project A的目录下。
15、多任务调用及排除任务的命令
//依次执行多个任务
gradle task1 task2 [...]
//-x后接的任务将不被执行
//例如,defaultTasks指定了task1, task2
//此时,task1和task2不会执行了
gradle -x task1 task2 [...]
16、文件树
文件树可以代表一个目录树结构或一个ZIP压缩文件的内容。
FileTree继承自FileCollection,我们可以像处理文件集合一样处理文件树。
FileTree的使用示例如下:
//以一个基准目录创建一个文件树
FileTree tree = fileTree(dir: 'src/main')
// 添加包含和排除规则
tree.include '**/*.java'
tree.exclude '**/Abstract*'
// 使用路径创建一个树
tree = fileTree('src').include('**/*.java')
// 使用闭包创建一个树
tree = fileTree('src') {
include '**/*.java'
}
// 使用map创建一个树
tree = fileTree(dir: 'src', include: '**/*.java')
tree = fileTree(dir: 'src', includes: ['**/*.java', '**/*.xml'])
tree = fileTree(dir: 'src', include: '**/*.java', exclude: '**/*test*/**')
// 遍历文件树
tree.each {File file ->
println file
}
// 过滤文件树
FileTree filtered = tree.matching {
include 'org/gradle/api/**'
}
// 合并文件树A
FileTree sum = tree + fileTree(dir: 'src/test')
// 访问文件树的元素
tree.visit {element ->
println "$element.relativePath ------ $element.file"
}
我们还可以使用ZIP或TAR等压缩文件的内容作为文件树,
Project.zipTree()和Project.tarTree()方法可以返回Project对应文件的FileTree实例。
示例用法如下:
// 使用路径创建一个ZIP文件
FileTree zip = zipTree('someFile.zip')
// 使用路径创建一个TAR文件
FileTree tar = tarTree('someFile.tar')
//TarTree可以根据文件扩展名得到压缩方式
//如果我们想明确的指定压缩方式则可以如下操作
FileTree someTar = tarTree(resources.gzip('someTar.ext'))
17、文件同步任务
同步任务(Sync)继承自复制任务(Copy)。
当执行时会复制源文件到目标目录,然后从目标目录删除所有非复制文件。
如下:
//将libs中的文件复制到test
//最终test目录中的内容与libs一模一样
task syncTask(type: Sync) {
from 'libs'
into 'test'
}
上一篇: 联想数位板绘图总是很卡顿该怎么办?