Android

Android的持续化集成及多版本打包

2017-11-15  本文已影响30人  Goorwl

文档概述

关于Android开发,除了技术方面需要掌握,还有发布流程需要了解。本文档就包括以上两个方面,主要介绍:

一、场景描述

在项目开发中,我们可能有多个功能有区别但是整体框架一致的工程。

情景:我们有一app,基础功能包括a,b,c,扩张功能包括d,e,f,其中客户1需要扩展功能d,f,客户2需要e,f。

二、配置文件简介

鉴于以上场景,开发app过程应该怎么做?如果客户1创建一份工程代码,客户2创建一份工程代码效率就太低了。因此我们需要另辟思路,采用配置文件的方式进行开发。

配置文件分类

关于配置文件目前有两种方式:

  1. apk文件写注释
  2. apk源码属性文件

配置方式的区别

方式1主要适用于轻量的注释,例如书写渠道名等一些简单的注释,不用修改源码。方式2就比较适合当前的场景,但是缺点在于配置文件在源码部分,所以修改配置文件就必须修改源码。

配置文件的使用

关于方式1的使用,可以: 美团批量打包

基本原理就是在apk文件生成之后,修改apk文件的部分字段,而不影响apk本身签名验证,在源码中根据apk安装的位置获取安装包文件再读取其中的字段。

方式2就是使用属性文件。实现方式就是使用java.util.Properties类进行文件加载。
预先在Android工程的main文件夹下创建assets文件夹,里面存放配置文件,客户1的配置文件命名为:a.propertiesb.properties,里面的内容如下:

fun_a=true
fun_b=true
fun_c=true
fun_d=true
fun_e=false
fun_f=true

读取配置属性代码:

public String funX(Context context, String x){
    Properties props = new Properties();
    try {
        props.load(context.getAssets().open("a.properties"));
    //  props.load(context.getAssets().open("b.properties"));
    } catch (IOException e) {
        e.printStackTrace();
    }
    return props.getProperties("fun_" + x);
}

在源码中根据对应函数的配置信息决定是否执行对应操作。

对于客户1和客户2的不同需求可以选择性的加载配置文件进行打包操作。

以上就是不同需求的具体操作。但是我们可以发现,这个操作效率太低,每次打包都要修改源码。下面介绍自动化过程。

三、 自动化之gradle打包

配置操作

为了之后可以执行自动化操作,签名必须也要能进行自动化执行。

首先放置签名文件到:项目的根目录。

modulebuild.gradle文件中添加以下内容:

apply plugin: 'com.android.application'

def keystorePSW = ''
def keystoreAlias = ''
def keystoreAliasPSW = ''
// default keystore file, PLZ config file path in local.properties
Properties properties = new Properties()
// local.properties file in the root director
properties.load(project.rootProject.file('gradle.properties').newDataInputStream())

keystorePSW = properties.getProperty("keystore.password")
keystoreAlias = properties.getProperty("keystore.alias")
keystoreAliasPSW = properties.getProperty("keystore.alias_password")

android {
    ...
    signingConfigs {
        release {
            keyAlias keystoreAlias
            keyPassword keystoreAliasPSW
            storePassword keystorePSW
            storeFile file('../xxx.jks')            // 签名文件的位置
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            zipAlignEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.release
        }
    }
}
dependencies {
    ...
}

为了能够在Jenkins环境也能使用自动打包操作,在项目的根目录gradle.properties文件中添加如下内容:

keystore.password = xxx             // 密钥的密码
keystore.alias = xxx                // 密钥的别称
keystore.alias_password = xxx       // 别称的密码

随后在项目的根目录下使用gradle即可进行打包。

执行命令

打包命令:

gradlew clean               // 清除build文件夹
// 二选一
gradlew build               // 检查依赖并编译打包,会生成debug和release两个包
gradlew assesmRelease       // 生成release包

执行以上命令之后,会在项目的app/build/output/apk/*.apk生成对应的apk。

注:Windows操作系统使用gradlew,Linux系统使用./gradlew

以上,使用gradle自动打包就以完成。下面就是集成Jenkins持续集成环境了。

四、Jenkins集成

创建项目

关于构建介绍可以参考:Jenkins+Gradle实现android开发持续集成、打包

配置信息没什么特别的。主要是输出文件路径:直接填写app/build/output/app/*.apk即可。

输出文件路径

可能遇到的问题

通过以上操作,Android项目就可以使用Jenkins自动集成了。

但是我们第一部分的场景问题还是没有解决,如何自动化区分客户的版本呢?

五、自动化版本区分

Android官网介绍了 构建变体

通过配置不同的 productFlavors我们可以获取不同版本的apk。

因此第一部分的需求通过以下操作实现。

更新buidl.gradle文件

android {
    ...
    buildTypes {
        ...
    }

    productFlavors {
        fun_a {
            buildConfigField "String", "CONF_NAME", "\"a.properties\""
        }
        fun_b {
            buildConfigField "String", "CONF_NAME", "\"b.properties\""
        }
    }
}

修改读取配置文件代码

配置属性文件不变,读取的代码进行如下修改:

public String funX(Context contex, tString x){
    Properties props = new Properties();
    try {
        props.load(context.getAssets().open(BuildConfig.CONF_NAME));
    } catch (IOException e) {   
        e.printStackTrace();
    }
    return props.getProperties("fun_" + x);
}

之后使用打包命令就会生成两个apk安装包,一个是客户1定制的功能,一个是客户2定制的功能。

输出路径不变,生成包名:

这样,Jenkins一次编译也就可以获取到正确的版本。

修改输出文件的包名

以上操作之后已经可以正确获取目标apk,但是并不直观。如果可以在输出文件名中添加输出版本号和打包时间就完美了。

在项目的build.gradle文件中,最后节点添加:

def releaseTime() {
    return new Date().format("yyyyMMdd-HHmm", TimeZone.getTimeZone("GMT+08:00"))
}
android{
    applicationVariants.all { variant ->
        variant.outputs.each { output ->
            def outputFile = output.outputFile
            if (variant.buildType.name.equals('release')) {
                def fileName = outputFile.name.replace("app-","").replace("release", "v${defaultConfig.versionName}-${releaseTime()}")
                output.outputFile = new File(outputFile.parent, fileName)
            }
        }
    }
}

修改之后输出文件名:

fun_a-v2.0.5-20171115-1655.apk
fun_b-v2.0.5-20171115-1655.apk

以上。

六、小结

通过使用gradle+Jenkins,可以让程序员从繁复的打包任务中解放出来,更多时间去做核心开发的相关业务。

gradle的功能强大到我没法想象,好好学习,好好钻研。

技术,可以解放你。

上一篇下一篇

猜你喜欢

热点阅读