Android自定义gradle插件保存打包信息

背景

公司的Android项目是在google play发布,由于项目组的历史原因,测试负责jenkins的打包操作(无论是测试阶段还是打release包),因为每次发版的负责人可能不一样,并且项目采用的是指定flavors去打不同国家(渠道)包,这就很难避免因为人为因素导致的打包出错(debug还是release,菲律宾还是越南版本等等)。所以这个插件的出发点是做到无论是谁打的包,是谁负责推到gp发布,都可以直接拿到发布apk的打包信息,通过打包信息去判断是否为最终发布版本。结合项目主要考虑插入以下信息来保证唯一性:

  • 国家版本,即我们熟知的flavors渠道号
  • buildType,即debug还是relase等等
  • 包名,在项目中不是必选项,因为上传gp如果包名不一致会上传失败
  • 版本号
  • git提交信息,包括打包分支和最近的提交commit id,格式 branch :commitId
  • 自定义额外信息,做扩展用

以上信息都可以自定义是否写入,默认写入。

流程

梳理了需要插入的打包信息后,接下来就是需要确定怎么插入。一开始想用最简单的方式,直接在zip的comment字段插入信息,直接读取就好了,但是发现项目已经用了v2签名,这种方法不适用;沿着这种思路可以考虑用美团的walle,但考虑到上传gp的稳定性和没有生成文件的直观方便,最终还是选择在META-INF插入自定义文件,同时拿到生成包后写一个python脚本去读。

grale插件

Talk is cheap,show me the code.这里介绍的是本地插件,没有push到maven。

1.定义常量

class InsertMetaInfConstants {

//定义gradle扩展名 
static final String EXTENSION_NAME = "insertmetainf"

//指定生成文件路径
static final String GENERATE_INSERT_PATH = 'generated/insertmetainf/'

}

2.gralde扩展参数配置

class InsertMetaInfExtension {

/**
 * 设置meta-inf文件名字
 */
String metaInfName = null

/**
 * 是否写入flavor,默认写入
 */
boolean insertFlavor= true

/**
 * 是否写入build type,默认写入
 */
boolean insertBuildType= true

/**
 * 是否写入applicaitionId,默认写入
 */
boolean insertApplicaitionId = true

/**
 * 是否写入versionName,默认写入
 */
boolean insertVersionName = true

/**
 * 是否写入versionCode,默认写入
 */
boolean insertVersionCode = true

/**
 * 是否写入该分支最后git的commit信息,默认写入
 */
boolean insertGitCommit = true

/**
 * 额外信息
 */
String[] insertExtraInfo = null

}

3.自定义gralde plugin

import com.android.build.gradle.*
import org.gradle.api.Plugin
import org.gradle.api.Project

class InsertMetaInfPlugin implements Plugin {

@Override
void apply(Project project) {
    project.extensions.create(InsertMetaInfConstants.EXTENSION_NAME, InsertMetaInfExtension)

    InsertMetaInfTask insertMetaInfTask = project.task("insertMetaInfTask", type: InsertMetaInfTask)

    def resDir = new File(project.buildDir, InsertMetaInfConstants.GENERATE_INSERT_PATH)
    def destDir = new File(resDir, 'META-INF/')

    boolean isLibrary = project.plugins.hasPlugin(LibraryPlugin)
    if (isLibrary) {
        project.extensions.getByType(LibraryExtension).sourceSets({
            main.resources.srcDirs += resDir
        })
    } else if (project.plugins.hasPlugin(TestPlugin)) {
        project.extensions.getByType(TestExtension).sourceSets({
            main.resources.srcDirs += resDir
        })
    } else if (project.plugins.hasPlugin(AppPlugin)) {
        project.extensions.getByType(AppExtension).sourceSets({
            main.resources.srcDirs += resDir
        })
    }
    insertMetaInfTask.setDestDir(destDir)

    project.afterEvaluate {
        project.tasks.findAll { task ->
            task.name.startsWith('generate') && task.name.endsWith('Resources')
        }.each { t ->
            t.dependsOn insertMetaInfTask
        }
    }
}
}

4.自定义gralde task

import com.android.build.gradle.internal.tasks.DefaultAndroidTask
import org.gradle.api.invocation.Gradle
import org.gradle.api.tasks.TaskAction

import java.util.regex.Matcher
import java.util.regex.Pattern

class InsertMetaInfTask extends DefaultAndroidTask {

def insertApplicaitionId = null

def insertVersionName = null

def insertVersionCode = null

def insertFlavor = null

def insertBuildType = null

InsertMetaInfTask() {
    setVariantName("InsertMetaInfTask")
}

private File destDir

void setDestDir(File destDir) {
    this.destDir = destDir
}

@TaskAction
def doExecute() {
    println("+-----------------------------------------------------------------------------+")
    println("|                      Insert Meta Inf Plugin START                            |")
    println("+-----------------------------------------------------------------------------+")
    InsertMetaInfExtension insertMetaInfExtension = project[InsertMetaInfConstants.EXTENSION_NAME]

    destDir.mkdirs()

    String metaInfName = insertMetaInfExtension.metaInfName
    if (null == metaInfName || metaInfName.length() <= 0) {
        throw new RuntimeException("metaInfName can not be empty!\n")
    }
    def vfile = new File(destDir, metaInfName)
    StringBuilder sb = new StringBuilder()
    //初始化默认的打包信息
    initCurrentPackageInfo()
    insertTask(sb, insertMetaInfExtension)

    def metaInfStr = sb.toString()
    vfile.text = metaInfStr

    println(
            "\nMETA-INF properties generated in ${destDir.getAbsolutePath()}${File.separator}$metaInfName: \n\n$metaInfStr")

    println("+-----------------------------------------------------------------------------+")
    println("|                       Insert Meta Inf Plugin END                             |")
    println("+-----------------------------------------------------------------------------+")
}

/**
 * 开始写入操作
 */
def insertTask(StringBuilder sb, InsertMetaInfExtension extension) {
    //写入基本信息
    writeBaseInfo(sb, extension)
    //写入额外信息,有的话
    writeExtraInfo(sb, extension)
}

def writeExtraInfo(StringBuilder sb, InsertMetaInfExtension extension) {
    String[] extraInfo = extension.insertExtraInfo;
    if(extraInfo != null && extraInfo.length > 0) {
        for(String info : extraInfo) {
            sb.append(info).append('\n')
        }
    }
}

def writeBaseInfo(StringBuilder sb, InsertMetaInfExtension insertMetaInfExtension) {
    getJenkinsBuildCode()
    if (insertMetaInfExtension.insertFlavor) {
        sb.append("flavor=").append(insertFlavor).append('\n')
    }
    if(insertMetaInfExtension.insertBuildType) {
        sb.append("buildType=").append(insertBuildType).append('\n')
    }
    if (insertMetaInfExtension.insertApplicaitionId) {
        sb.append("packageName=").append(insertApplicaitionId).append('\n')
    }
    if (insertMetaInfExtension.insertVersionName) {
        sb.append("versionName=").append(insertVersionName).append('\n')
    }
    if (insertMetaInfExtension.insertVersionCode) {
        sb.append("versionCode=").append(insertVersionCode).append('\n')
    }
    if (insertMetaInfExtension.insertGitCommit) {
        sb.append("gitCommit=").append(getGitBranch() + " : " + getLastCommitId()).append('\n')
    }
}

/**
 * 获取打包分支
 * @return
 */
def getGitBranch() {
    return 'git symbolic-ref --short -q HEAD'.execute().text.trim()
}

/**
 * 获取最近的git commit id
 * @return
 */
def getLastCommitId() {
    return 'git rev-parse --short HEAD'.execute().text.trim()
}

def initCurrentPackageInfo() {
    def currFlavor = getCurrentFlavor()
    def test = project.extensions.findByName("android")
    test.productFlavors.all { flavor ->
        if (flavor.name.equalsIgnoreCase(currFlavor)) {
            insertApplicaitionId = flavor.getApplicationId()
            insertVersionCode = flavor.getVersionCode()
            insertVersionName = flavor.getVersionName()
        }
    }
}

def getCurrentFlavor() {
    Gradle gradle = project.getGradle()
    String tskReqStr = gradle.getStartParameter().getTaskRequests().toString()
    Pattern pattern
    if (tskReqStr.contains("assemble")) {
        pattern = Pattern.compile("assemble(\\w+)(Release|Debug)")
    } else {
        pattern = Pattern.compile("generate(\\w+)(Release|Debug)")
    }
    Matcher matcher = pattern.matcher(tskReqStr)
    if (matcher.find()) {
        insertFlavor = matcher.group(1).toLowerCase()
        insertBuildType = matcher.group(2).toLowerCase()
        return insertFlavor
    } else {
        println "NO MATCH FOUND"
        return ""
    }
}

/**
 * 获取Jenkins Build 号
 * @return
 */
def getJenkinsBuildCode() {
    ext.env = System.getenv()
    ext.buildNumber = env.BUILD_NUMBER?.toInteger()
    def test = "$buildNumber"
    println("test buildNumber: " + test)
    return
}
}

5.使用插件

//导入插件路径
apply plugin: 'com.***.insertmetainf'

insertmetainf {
    metaInfName 'packageInfo.properties'
    insertVersionCode false
    insertExtraInfo "test1=gg", "test2=pp"
}

6.生成文件信息例子

flavor=ph
buildType=debug
packageName=com.***
versionName=1.1.5
gitCommit=master : d113b9ab
test1=gg
test2=pp

pyhton读取脚本

读取就比较简单了,直接用zip去获取apk里面的META-INF/packageInfo.properties文件:

#coding:utf-8

'''
Auth:bottleTan
Date:2019-08-12

This is a tool for read apk meta-inf/packageInfo.properties file
for more details, please contact [email protected]
'''

import sys
import os
import zipfile

target_file = 'META-INF/packageInfo.properties'

def read(apk_path):

z = zipfile.ZipFile(apk_path, 'r')
try:
    print("========================================================")
    print(z.read(target_file))
    print("========================================================")
except Exception:
    raise RuntimeError(target_file + ' is not exists or other exception.')

if __name__ == '__main__':
    command = sys.argv
    if len(command) < 2:
        raise ValueError("command line params must have 2 parameters at least.")

apkPath = command[1]
if not os.path.exists(apkPath):
    raise IOError(apkPath + " is not exists,please check.")

print(apkPath)
read(apkPath)

至此,整个流程还是比较简单的,代码也是相对清晰。源码基本如上,就不放到github上了。

你可能感兴趣的:(Android自定义gradle插件保存打包信息)