studio(intellij)+gradle(1.0+)+jenkins 打包&上传私服

现在比较流行使用gradle来配置项目,本文着重介绍studio和intellij打包。

在Android gradle项目中project类似于eclipse的workspace,而moudule类似于eclipse的project。

demo svn位置:http://10.3.254.91/svn/mobile/android/GradleTest

demo jenkins项目名称:Android_GradleTest

选择gradle包

那么基于gradle的project下会有一个gradle包,里面包含一个wrapper/gradle-wrapper.properties,指定了本project的gradle配置

distributionBase=GRADLE_USER_HOMEdistributionPath=wrapper/distszipStoreBase=GRADLE_USER_HOMEzipStorePath=wrapper/dists

distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip

这段话指定了gradle的下载地址,和gradle的位置,GRADLE_USER_HOME一般指向${user}/.gradle,大家有兴趣可以自己查看下这个目录,当你换gradle版本时,而studio和intellij不使用本地gradle时,会自动下载gradle包到该目录下,有时会因为缓存的原因导致gradle包反复下载,这时推荐指定使用本地的gradle(在国内FQ下载速度不给力,大家都去国外打工把)

Project的gradle配置文件

  • build.gradle  主要gradle配置文件,指定buildScript的仓库和依赖(这个依赖不是指应用的jar活着其他工程依赖,而是指主要依赖编译gradle插件的依赖,比如依赖android-gradle插件)
    dependencies {classpath ‘com.android.tools.build:gradle:1.1.1‘} 这段话就是引入android的gradle插件,这个插件是android官方专门为配置android的工程写的插件,继承java插件,因此在后面引入apply plugin: ‘com.android.application‘ 或者apply plugin: ‘com.android.library‘不能再引入java plugin。其中后面的版本号1.1.1不是指的gradle版本,而是指的此插件的版本,该插件版本和studio版本号是一致的,其中不同studio对最低的gradle做了要求,具体见 http://tools.android.com/tech-docs/new-build-system。而在gradle文件中为了方便还能指定allprojects或者subprojects的闭包(即加个大括号"{}"),闭包中一般将子moudule或者所有moudule共同的代码写在这里,最常见的是定义仓库(即jar、aar该从哪下)。
  • gradle.properties project的gradle属性文件,gradle在加载时会读取该文件,该文件可以自定义想在.gradle读取到得属性,还可以指定gradle的一些属性,比如configureOnDemand、jvm参数、开启daemon进程、Parallel运行,但是测试的时候发现jvm参数和daemon进程都会造成编译失败,因此只开启多线程运行,实际测试中多线程运行并没有带来多大性能变化,而且这个属性在gradle语法被标记为incubating,表示为非正式的,以后很可能改变的,加不加上看个人喜好吧,加上的语法是org.gradle.parallel=true(如果是多module上传包,会因为pom冲突发生错误,正常情况下还是建议不使用)。如果是在本机测试,daemon属性依旧可以不用加,因为studio已经默认开启了,这个daemon线程一旦开启,将会持续3个小时,如果不想开启,可以加上org.gradle.daemon=false;但比较推荐自己修改jvm参数,默认生成的properties文件会有行注释,把那行注释去掉即可:
    #org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
    
  • settings.gradle 该文件指定project下面有多少个moudule,如果moudule不在这里被包含相当于对该project隐藏,最简单定义方式如下:
          include ‘:app‘, ‘:DependLib‘
  • local.properties  该文件目前之时用来指定sdk的路径可以设为sdk.dir或者ANDROID_HOME,如果不指定jenkins会搜寻机器默认的sdk,即环境变量中制定的sdk
  • 其他*.gradle文件或者*.properties,gradle允许用户自定义gradle脚本文本,比如提取公共模块,需要加载时通过apply from:’a.gradle’来引入,properties文件一般用来定义键值属性,在gradle可以通过下面语法加载:
Properties properties = new Properties()properties.load(project.rootProject.file(‘common.properties‘).newDataInputStream())

ext {    repositoryUrl = properties.getProperty("repositoryUrl”)}

Module的gradle配置文件

一个新建的module默认只有一个build.gradle文件,这个gradle文件里面主要配置该module的一些基本信息,比如属于application或者library,指定android基本编辑信息,配置build信息,jdk变异版本,签名信息,渠道包等等,简略介绍如下:

compileSdkVersion (int),编译使用的android sdk版本,对应平常我们知道的API 版本。

buildToolsVersion (String),编辑具体的version code号,可以去sdk manager查找已经下载的版本

defaultConfig (closure),默认的编译属性,具体包括:

  • applicationId (string)应用id,也是包名路径
  • minSdkVersion (int)支持最低sdk版本
  • targetSdkVersion (int) 目标编译sdk版本,该属性会影响android部分展示,譬如targetSdkVersion在10以下,android底部虚拟nav bar最右边会有个小菜单图标
  • versionCode (int) 本module的版本code
  • versionName (string)本module的版本name

compileOptions (closure)用来指定jdk编译版本

signingConfigs (closure)定义一个签名文件

buildTypes (closure)构建实例,默认有release和build两种,且不可修改;构建实例中具体常见包含有用参数如下(还可以修改applicationId后缀等等):

  • minifyEnabled (boolean)是否混淆
  • proguardFiles (file[])混淆文件
  • signingConfig (ref)使用签名文件
  • zipAlignEnabled (boolean)是否对apk包优化

sourceSets.main (closure)指定或者重定向android资源文件夹,不建议使用,建议采用gradle默认目录,即jni,jniLibs,aidl,java,rs,assets文件夹和manifest文件均置于studio规定的gradle工程默认目录,即src/main 下。其中jni和jniLibs是不同的文件夹,jni用来指定需要ndk编译的资源,即c++源文件和manifest;jniLibs是编译好的so包文件,可以直接使用

lintOptions (closure),指定lint任务的一些属性,一般主要指定 abortOnError false ;ignoreWarnings true

productFlavors (closure),用于设置多个渠道包,其基本参数和buildType类似

applicationVariants application插件的参数,具体参照http://tools.android.com/tech-docs/new-build-system/user-guide

libraryVariants library插件的参数,具体参照http://tools.android.com/tech-docs/new-build-system/user-guide

testVariants test插件的参数,具体参照http://tools.android.com/tech-docs/new-build-system/user-guide

除了该build文件之外,还可以新建一个gradle.properties文件,在grade运行时会默认加载该文件,我们可以把一些常见的属性丢到该文件,

最佳实践:打包时调用uploadArchives时需指定pom的group,artifact_id,version,packaging等,我们可以把这些属性写在各自module的grade.properties中,键值一致,譬如:

POM_NAME=dependPomPOM_DESCRIPTION=this is just test,do not pay more attention to ifPOM_GROUP=cn.myapplicationPOM_ARTIFACT_ID=dependPOM_PACKAGING=aar

POM_VERSION=1.0

然后将uploadArchives方法写到一个common.gradle中,如下:


uploadArchives {    repositories {        mavenDeployer {

            repository(url: rUrl) {                authentication(userName: rUser, password: rPass)            }

            pom.project {                name POM_NAME                groupId POM_GROUP                artifactId POM_ARTIFACT_ID                description POM_DESCRIPTION                version POM_VERSION                packaging POM_PACKAGING            }        }    }}

最后哪个module需要上传引入该gradle文件即可,即调用apply from:’common.gradle‘

配置仓库

仓库的定义一般只需在project的gradle文件中的buildscript和all projects模块中各定义一个即可,不推荐在module中还重复定义,我们一般只使用maven仓库,定义格式如下

repositories {

mavenLocal()

maven {
            url "http://nexus.laohu.com:8081/nexus/content/groups/android_public/"

}

maven {
            url ‘http://repository.sonatype.org/content/groups/public/

}

maven {
            url ‘http://repository.jboss.com/maven2/

}

maven {
            url ‘http://maven.oschina.net/content/groups/public/

}

jcenter()

}

gradle文件会根据定义的仓库依次寻找,在一个仓库里找不到再寻找下一个,其中mavenLocal()是存在本地的maven的仓库,jcenter是binatry的官方仓库。一般建议,将连接速度快的仓库、比较全的放在前头,可以减少网络连接时间。对于大多数仓库都建立在国外,速度慢吞吞,本实例给出了三个代理仓库,速度勉强还可以,但是速度仍然算不上客观。

因此我利用我们的私服搭建了我们自己的android仓库,其地址为"http://nexus.laohu.com:8081/nexus/content/groups/android_public/“,当我们请求资源时会向这个代理仓库请求,如果资源存在,则直接下载,如果不存在,那么代理仓库会向其代理的源地址请求,下载好存在代理仓库中再转给我们。使用项目私服仓库需使用host,如果谁有兴趣管理仓库的话可以通过下面地址和用户密码访问我们的私服。

访问地址: http://nexus.laohu.com:8081/nexus/

用户名密码: *******/******

理论上仓库是越多越好,但是在一种情况下例外,就是所需的资源在所有的仓库中都不存在,那么会花费大量时间去查找所有仓库的索引,造成编译过慢。比如android support repositories,当需要使用v4或者v7的资源包(aar),如果集成在studio或者intellij中,我们的插件会优先从本地安装的support仓库中寻找,并不会耗费时间。但是当集成在jenkins上用gradle打包的时候,gradle会按照仓库顺序查找而最后搜寻本地support仓库,这样会造成大量时间损耗,尤其是在我们内网链接jcenter或者mavenCenter时,效率想当低下。这时解决方案有两个:

  1. 再添加一个本地maven仓库,置于mavenLocal()后,maven{url "E:\\android-sdk_r21-windows\\android-sdk-windows-new\\android-sdk-windows\\extras\\android\\m2repository"},但这样的话需在不同的机器适配地址,需要不断的更改地址,当然可以该url前半部分改成$ANDROID_HOME来做兼容,不过貌似我在适配mac,windows和linux时候并不能一直保证成功,如果谁做成功了可以告诉我直接编辑给出较为满意的答案
  2. 减少仓库数,只保持连接速度较好的mavenLocal和我们的私服仓库,这样查询索引也是秒钟级别的,为什么能这么做?因为配置的私服是个group仓库(即是个仓库集合),其包含了jcenter和mavenCenter的代理仓库,根本无需再制定jcenter(),mavenCenter()或者其他的仓库了,从私服仓库中我们一定能获取到后面仓库里的内容,这样做的缺点是只能在内网使用。如果是在公司内部使用,因此建议配置仓库如下

     repositories {

             mavenLocal()

             maven {

                 url "http://nexus.laohu.com:8081/nexus/content/groups/android_public/"

             }

         }

 

最佳实践:私服配置成功后,下载依赖项相当快,为了避免在svn中libs中传递耗费时间,不建议将jar包放入libs然后项目再指定引用,因为libs必须要跟随svn同时传输,而在114的jenkins由于svn版本问题(后续建议采用git),每次必须要获取一份新的copy下来,这样就很耗费时间。而且引用的jar包不能为aar格式,无法兼容。配置依赖项如下:

     compile ‘com.squareup.okhttp:okhttp:2.0.0‘

 

上传项目到私服仓库

其基本设置在module的properties的最佳实践提到,这里给出一些具体解释,在uploadArchives,我们采取了以下字段:

repository(url: rUrl) {authentication(userName: rUser, password: rPass)} 这里repository即代表远程仓库,rUrl是远程仓库地址,我们第三方私服的地址为"http://nexus.laohu.com:8081/nexus/content/groups/android_3rd/“(不是上文的那个仓库,注意!!!),rUser是验证用户名,rName是密码。

定义pom如下

pom.project {

name POM_NAME
                groupId POM_GROUP
                artifactId POM_ARTIFACT_ID
                description POM_DESCRIPTION
                version POM_VERSION
                packaging POM_PACKAGING

}

name是pom的名称,groupId可以认为是该文件的包名,artifactId可以认为是该文件的具体名称,description即描述,version是该id的version,packaging是扩展名,如果不指定一般为jar,application工程为apk,library工程为war。一般一个pom唯一有groupId,artifactId和version唯一定义,这三个字段必不可少。

当按以上定义好了之后,在终端执行gradle uploadArchives,即可把我们项目发布到私服中,那么通过gradle引用已经发布的文档定义为 compile ‘groupId:artifactId:version(@packaing)’。发布到jcenter官网中需要自己去申请用户名和密码,还要在gradle文件中加上如下话,表明还要发布自己的源码和javadoc:

ask androidJavadoc(type: Javadoc) {
    source = android.sourceSets.main.java.srcDirs
    exclude ‘**/pom.xml‘
    exclude ‘**/proguard_annotations.pro‘
    classpath += files(android.bootClasspath)
}

task androidJavadocJar(type: Jar, dependsOn: androidJavadoc) {
    classifier = ‘javadoc‘
    from androidJavadoc.destinationDir
}

task androidSourcesJar(type: Jar) {
    classifier = ‘sources‘
    from android.sourceSets.main.java.srcDirs
}

artifacts.add(‘archives‘, androidJavadocJar)

artifacts.add(‘archives‘, androidSourcesJar)

发布成功后,最后需要等待jcenter官网审核,审核过了我们的project就可以存在jcenter中,外界也可以通过gradle方式引用了,我没试过,T_T,等人来炫耀

替换渠道包的变量

在渠道包我们会经常需要替换manifest文件中的一些变量,最常见的是替换UMENG_VALUE,在过去我们代码是在applicationvariants自己读取manifest,然后更改manifest中变量复制到新的文件夹,为应用重新制定manifest文件,但这样不是必须的,因为这样相当于读取了两遍manifest,一遍是插件自带的任务,一遍是我们给的任务;而且当需要替换多个字符串时,会多写很多代码。从gradle 0.5还是0.几的版本的插件已经给出了一个简单的解决方案,在读取时可以根据我们的设置去替换manifest的值。具体写法如下:

<meta-data android:name="channel" android:value="${UMENG_CHANNEL}" />

<meta-data android:name="TEST" android:value="${test}" />

manifest文件将需要替换的变量以美元符号“$”加大括号包含起来,然后在gradle文件中的android闭包中写上:

productFlavors {

WanDouJia { versionName ‘alpha‘ }

mc {manifestPlaceholders = [UMENG_CHANNEL: name, test:’tt‘]}

}
productFlavors.all { flavor ->
    flavor.manifestPlaceholders = [UMENG_CHANNEL: name, test: name]

}

即添加manifestPlaceHolders=[]这样的代码,中括号即是自己想要替换键值对,其中键对应manifest中${}里面的东东,在渠道包使用name,这个name是渠道包的名称,即productFlavors中定义的WanDouJia { versionName ‘alpha’ }中的WanDouJia,渠道包的name是个final常量,不可以更改,现在这么写过后,应该是以前的代码简单很多吧

替换apk名

在打包的时候,为了便于区分我们要针对不同的渠道包和构件号形成我们自己的规则的apk名,目前来说就需要自己动手在代码中实现替换包名。

一般插件默认生成的包名形式如下:模块名(settings.gradle文件中include的名称)-flavors-buildtype.apk。我们一般需要加上versionName和svn版本。

首先获得svn版本通过如下代码获得,首先通过gradle执行svn info命令获得字符串,然后通过正则表达来匹配版本号赋值给svnBuildNum,具体我也不是很清楚:

task svninfo() {
    new ByteArrayOutputStream().withStream { os ->
        def result = exec {
            executable = ‘svn‘
            args = [‘info‘]
            standardOutput = os
        }
        def outputAsString = os.toString()
        def matchLastChangedRev = outputAsString =~ /Last Changed Rev: (\d+)/
        svnBuildNum = "${matchLastChangedRev[0][1]}"
    }

}

然后在applicationVariants重命名输出文件名如下即可:

applicationVariants.all { variant ->
    variant.outputs.each { output ->
        if (hasSVN) {
            output.outputFile = new File(
                    output.outputFile.parent,
                    output.outputFile.name.replace(".apk", "-" + defaultConfig.versionName + "-r" + svnBuildNum + ".apk"))
        } else {
            output.outputFile = new File(
                    output.outputFile.parent,
                    output.outputFile.name.replace(".apk", "-" + defaultConfig.versionName + ".apk"))
        }
    }

}

提高打包效率

在gradle中任何行为都是划分为task的,然后再进一步划分为task中的action。而插件为我们默认构造好了很多task,我们只需调用即可。

比如我们在studio运行时会依赖于android 的build任务,这个任务会完成打包apk和一些check。但是完全依赖于默认配置会造成一些额外开销。

譬如:build任务依赖于assemble和check两个任务,前面的任务是打包,后面的任务主要是完成一些检查和测试,在我们实际情况中并不需要用到check。assemble任务又会分为assemebleRelease和assembleBuild两个任务,分别标明Realease和Build时的打包情况。因此我们绝大多是情况可以只调用assembleRelease,这样就可以省略assemebleDebug和check两个任务。

最佳实践:

task copyApk(type: Copy) {
    from ‘build/outputs/apk‘
    into ‘apks‘
    exclude ‘**/*-unaligned.apk‘
}

task buildRelease(dependsOn: [clean, assembleRelease, copyApk, svninfo]) {
    assembleRelease.mustRunAfter svninfo
    copyApk.mustRunAfter assembleRelease
    clean.mustRunAfter copyApk
}
task buildDebug(dependsOn: [clean, assembleDebug, copyApk]) {
    copyApk.mustRunAfter assembleDebug
    clean.mustRunAfter copyApk

}

这里定义了三个主要task,其中buildRelease和buildDebug就是我们推荐的主要的两个打包构建任务,对应的就是release和build。其中buildRelease任务依赖clean(清理输出控件,默认清理build中间的文件,这里会包含输出文件和中间临时文件),assembleRelease(打包),copyApk(将输出的文件输出到新文件夹,以防止被clean清理,其中不包含unaligned.apk),svninfo(获取svn信息);即buildRelease任务必须在这四个任务后运行,然后buildRelease中又规定了一些任务的运行顺序。这样本地的构建配置基本就完成了,最后一部分就是我们自己服务器的jenkins配置

jenkins配置

我们的jenkins分为34和114的jenkins,34jenkins执行在207的服务器上,114jenkins执行在13上,即需要在这里指定

PS:无论哪个jenkins里的gradle和android sdk都读取于服务器的环境变量

首先在jenkins上新建job,然后配置源码管理,android这边目前还是采用svn,这里有个check-out style,34由于某些原因(下面说)必须要选择Always check out as a fresh copy,这样每次都会从svn读取,效率会更低;114可以选择Use ‘svn update’ as much as possible

下面就可以选择构建任务:在构建时,34必须优先添加一个execute shell,command 填写svn upgrade。这个原因和上文选择check-out style原因一样,因为jenkins的svn插件只支持到1.7,而207上使用的svn版本是1.8,这样会造成svn格式不一致,因此在构建前必须调用svn upgrade将format格式统一一致,在低版本的svn库上对svn进行升级。114不需要选择这一步。

接下来增加构建任务Mobile App Build,一般情况下在打包下选择android,构建参数选择buildRelease -s而不是大家都使用的build -s;输出路径为app/apks为copyApk中指定的输出路径,如下图所示:

但是在114上构建使用的gradle版本是1.10,不适用新版本要求至少2.1以上;34使用的gradle版本是最新2.4,不用担心,要指定本地其他gradle版本构建时,那么在打包时不选用Android,而是直接选择Command Line,输入本地gradle执行文件路径和参数,如下所示

最后保存,就可以进行相关打包。

理论上,采用这种方式打包速度可以提高1倍,实际测试中,采用原始方法在本地打包速度在70-80s左右,采用本文方法打包速度在35-45s左右;而且建议采用34jenkins大于114jenkins,114jenkins插件版本较老,速度有很大折扣大概90s左右;而34jenkins上svn同步大概8s左右(建议让天心把207服务器上的svn版本降级,免我们在svn上有额外的消耗),打包速度在30-50s之间。

如果大家采用新版gradle打包的话,可以直接把我demo的代码复制过去稍微配置一点就可以使用,有疑问可以随时和我讨论;而且因为我本身水平的限制,可能有些观点并不正确,也希望大家矫正。

时间: 2024-10-06 07:28:31

studio(intellij)+gradle(1.0+)+jenkins 打包&上传私服的相关文章

Jenkins打包上传至远程服务器

一,设置远程服务器信息 点击高级,设置远程服务器密码等信息. 二,打包上传 在配置页,构建模块,选择如下: 配置上传文件以及上传后执行的脚本 三.保存配置,打包测试. 原文地址:http://blog.51cto.com/bluehumor/2124519

iOS开发进阶 - 使用shell脚本自动打包上传到fir.im上

如果移动端访问不佳,可以访问我的个人博客 用fir.im测试已经好长时间了,感觉每次打包上传都很麻烦,想着是不是可以用脚本自动打包,在网上搜了一下确实有,下面总结一下如何使用脚本自动打包上传到fir.im,以及打包过程中遇到的问题和解决办法 相关资料和下载 首先是打包脚本的下载地址,这个是我找到的比较全的一个,里面有很多不同功能的shell脚本,亲测好用,传送门 还有关于fir指令的一些介绍可以去这里查看,传送门 第一步安装fir-cil fir-cli 使用 Ruby 构建, 无需编译, 只要

xcode7 打包上传至 app store

文章参考自: http://www.cnblogs.com/WayneLiu/p/4993391.html?utm_source=tuicool&utm_medium=referral http://blog.csdn.net/jeepxiaozi/article/details/43373243 在xcode7以前, 用户是不能直接真机调试的, 必须有开发者帐号才可以真机! xcode7出现后, 用户可以直接把app 安装在自己的手机上面. 真机调试步骤: 1=====>首先你得向appl

在pom.xml中使用distributionManagement将项目打包上传到nexus私服

本文介绍 如何在pom.xml中使用distributionManagement将项目打包上传到nexus私服 1.pom.xml文件添加distributionManagement节点 <!-- 使用分发管理将本项目打成jar包,直接上传到指定服务器 --> <distributionManagement> <!--正式版本--> <repository> <!-- nexus服务器中用户名:在settings.xml中<server>的

[Xcode10 实际操作]九、实用进阶-(32)项目的打包上传和提交审核以及下架处理

本文将演示如何将一个应用程序进行打包上传,并提交审核以及下架处理. 点击项目[DemoApp]->[Build Settings]编译设置->[Provisioning Profile]证书设置区域. 在[苹果开发者管理后台],创建应用程序的开发证书和发布证书. [Debug]:首先设置应用程序的开发证书,有了开发证书后,可以在真机设备上运行和测试应用程序. 在弹出的证书列表中,列出了所有的开发证书和发布证书,这里选择开发证书. [Release]:设置应用程序的发布证书,发布证书可以让您对应

java~gradle构建公用包并上传到仓库~使用私有仓库的包

在新的项目里使用仓库的包 上一讲中我们说了java~gradle构建公用包并上传到仓库,如何发布公用的非自启动类的包到私有仓库,而这一讲我们将学习如何使用这些包,就像我们使用spring框架里的功能包一样. 参考:http://www.zhyea.com/2018/04/24/gradle-repository-username-password.html?spm=a2c40.rdc_maven_repo.0.0.12fd3054jv5EgP 公司私有的maven仓库在访问时是需要用户名密码的.

【Vue中的坑】Vue打包上传线上报Uncaught SyntaxError: Unexpected token &lt;

今天在vue打包上传线上后,报一下错误,一下就懵了,这可咋整啊,一如既往的想都没想就开始复制错误,上网开搜 Uncaught SyntaxError: Unexpected token < Uncaught SyntaxError: Unexpected token < Uncaught SyntaxError: Unexpected token < 网上搜的大致有几种 1.因为vue在打包上传的时候不会编译es6,需要安装babel来将es6转成es5 . 2.在经过build/web

[Servlet3.0]Serlvet文件上传

Servlet 3.0的另一个新特性就是提供了处理文件上传的功能,使用Servlet 3.0的内容实现文件上传需要以下几个内容: 在处理文件上传的Servlet上增加@MultipartConfig注解,表示当前Servlet符合MIME类型的multipart/form-data. Optional Element Summary int fileSizeThreshold java.lang.String location long maxFileSize long maxRequestSi

【Linux命令】--(4)文件打包上传和下载

文件打包上传和下载 +++++++++++++++++++++++++++++++用SecureCRT来上传和下载文件tar命令gzip命令+++++++++++++++++++++++++++++++先把计划放着,待老夫国庆把它学完!