涉及知识点

  • Jenkins
  • Android Studio(Gradle)
  • 蒲公英

Jenkins

我的理解,就是可以帮我们减少重复工作的一个流程化执行工具。详见百度

安装 Jenkins

我使用了最简单方式,下载纯的 war 文件,然后使用 java -jar jenkins.war 的方式执行

我用的 macOs 系统,至于为什么不使用 dmg 安装的后面补充说明

配置 Jenkins

  • 默认访问:http://localhost:8080/ 就可以打开 Jenkins
  • 解锁:cat ~/.jenkins/secrets/initialAdminPassword 输入密码,建议后续使用这个密码登录 admin 后修改 admin 密码
  • 安装插件:可以在第一次解锁配置后安装,也可以后面用到再安装
  • Gradle Plugin
  • Git plugin
  • description setter plugin

插件管理->立即获取 操作中你可能会遇到 ssl 握手失败问题,有经验的人看下错误的堆栈信息就知道是这个问题。 Fix:“高级” 这个tab,把升级站点的 https 修改为 http

创建 Jenkins 打包任务

  • 名字随意,选择:构建一个自由风格的软件项目
  • general tab:我选择了“参数化构建过程”,添加了两个文本参数 verName,verCode,为了以后可能会需要再不改变代码的情况下打一个升级了版本名和版本号的新包
  • 源码管理,安装了 Git plugin 后会出现 git 选项,填入你的远程仓库地址,构建的分支,私有库还需要授权信息

在使用 dmg 安装的前提下,在私有库填入了正确的授权信息拉取时,可能会遇到 fetch stuck 的问题,你会看到很多以 # timeout=10 结尾的日志,并且一直卡在 clone 上

解决方案:正如前面提到的,卸载 dmg 安装的 Jenkins,然后使用 war 的。原因在https://groups.google.com/forum/#!topic/jenkinsci-users/L23XG2LLha0 已经有人解释清楚了,结论就是 macOs 下的 user 不一致

  • 构建触发器,可以不选择,只在自己需要构建的情况下使用:Build with Parameters 构建
  • 构建,这里我用到了 Gralde Plugin 和 蒲公英提供的 API。如下图所示
  • 提测我只构建了 Alpha 版本的包
  • 写了一个 shell,在 app/ 这个目录下找到我构建的 apk,然后使用 curl 以 form 的形式通过 API 提到蒲公英以便给测试下载提测
  • 注意:这里截图里没有的,要传递参数到 build.gradle 里面处理必须在 Invoke Gradle script 这一栏的“高级”里面勾选上 “Pass all job parameters as System properties”
  • 最后,我定义了归档文件夹,以及使用了 description setter plugin,抽取了 curl 提交后返回的下载页面短地址

build.gradle 代码

apply plugin: 'com.android.application'

android {
    def verName = System.getProperties().getProperty("verName")
    def verCode = System.getProperties().getProperty("verCode")

    def alphaVersionName = verName ? verName : alphaVerName
    def alphaVersionCode = verCode ? verCode : alphaVerCode

    println alphaVersionName
    println alphaVersionCode

    // 以上是我获取到构建参数的代码

    compileSdkVersion 27
    defaultConfig {
        applicationId "xxxx"
        minSdkVersion 14
        targetSdkVersion 22
        versionCode 1
        versionName "1.0.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

        buildConfigField('String', 'HOST', '\"xxx\"')

        manifestPlaceholders = [
                GETUI_APP_ID    : "xxx",
                GETUI_APP_SECRET: "xxx",
                GETUI_APP_KEY   : "xxx",
                UMENG_APP_KEY   : "xxx",
                UMENG_CHANNEL   : "android"
        ]
    }

    signingConfigs {
        release {
            storeFile file('../xxx.jks')
            storePassword bcStorePassword
            keyAlias bcKeyAlias
            keyPassword bcKeyPassword
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.release
        }
        debug {
            ext.enableCrashlytics = false
            signingConfig signingConfigs.release
        }
    }

    dexOptions {
        preDexLibraries true
        maxProcessCount 8
        javaMaxHeapSize '2g'
    }

    flavorDimensions("app")

    productFlavors {
        dev {
            applicationIdSuffix '.dev'
            versionName "2.0.6"
            minSdkVersion 21
            buildConfigField('String', 'HOST', '\"xxx\"')
            manifestPlaceholders = [
                    GETUI_APP_ID    : "xxx",
                    GETUI_APP_SECRET: "xxx",
                    GETUI_APP_KEY   : "xxx",
                    UMENG_APP_KEY   : "xxx",
            ]
        }
        alpha {
            versionCode alphaVersionCode as Integer //注意这里的处理
            versionName alphaVersionName
            buildConfigField('String', 'HOST', '\"test.xxx\"')
            manifestPlaceholders = [
                    GETUI_APP_ID    : "xxx",
                    GETUI_APP_SECRET: "xxx",
                    GETUI_APP_KEY   : "xxx",
                    UMENG_APP_KEY   : "xxx",
            ]
        }

//        xiaomi {}
//        vivo {}
//        c360 {}
//        tencent {}
//        baidu {}
//        others {}
//        fenx {}
//        weixin{}
//        huawei{}
    }

    android.applicationVariants.all { variant ->
        variant.outputs.all {
            outputFileName = "xx-${variant.name}-${variant.versionCode}-${variant.versionName}.apk"
        }
    }

    productFlavors.all { flavor ->
        if (flavor.name != 'dev' && flavor.name != 'alpha') {
            flavor.manifestPlaceholders = [UMENG_CHANNEL: name]
        }
    }

}

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation 'com.android.support:appcompat-v7:27.0.2'
    implementation 'com.android.support.constraint:constraint-layout:1.0.2'
    implementation 'com.android.support:support-v4:27.0.2'
    implementation 'com.android.support:design:27.0.2'
    implementation 'com.android.support:cardview-v7:27.0.2'
    implementation 'com.android.support:recyclerview-v7:27.0.2'
    implementation 'com.github.chrisbanes:PhotoView:1.3.1'
    // test
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.1'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
    // third part
    implementation 'com.jakewharton:butterknife:8.8.1'
    annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
    implementation 'com.getui:sdk:2.10.2.0'
    implementation 'com.umeng.analytics:analytics:latest.integration'
    implementation files('libs/SocialSDK_QQ_Simplify.jar')
    // local lib
    implementation project(path: ':swipeloadlayout')
    implementation project(path: ':httplib')
    implementation project(path: ':glide')
    implementation project(path: ':chart')
    implementation files('libs/SocialSDK_QQ_Simplify.jar')

    implementation('cn.qqtheme.framework:WheelPicker:1.4.4') {
        exclude group: 'com.android.support'
    }
}