Android Gradle学习记录6 代码记录(持续补充)

来源:互联网 发布:韩国工业 知乎 编辑:程序博客网 时间:2024/05/16 13:47

本篇博客主要记录一些,自己觉得比较有价值的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 Doctask 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 copySdkDoctask 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为需查找的keydef 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/**'}// 合并文件树AFileTree 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'}
原创粉丝点击