Android Studio gradle配置

Posted by JamesPxy on 2017-01-11

1.使用配置文件管理app版本

因为Android Studio是使用gradle进行项目构建的,这使得通过配置文件进行版本管理成为可能。

使用配置文件管理app版本很简单,就是定义一个properties文件,里面有版本号、版本名等版本信息,只需要在build.gradle中引用该文件,使用该配置文件的属性,进行项目的版本号等版本信息的赋值,就可以实现版本号的动态控制(**注意:**在gradle文件中配置的版本号、版本名称是优于在manifest.xml中配置的,如果你在gradle文件中配置了版本信息,那么不管你是否也在manifest.xml文件中进行了配置,系统都不会再去manifest.xml中进行版本信息的读取了)。

要实现这个目标,首先,要新建一个properties文件(或者直接在android studio工作控件下的local.properties文件中进行配置也可以,本文的例子就是在该文件中进行配置的),内容如下:

	#local.properties文件中自己定义的sdk位置,本来就有
	sdk.dir=D\:\\Android\\sdk
	#=====以下是自己定义的内容=====
	# 打包的输出路径
	appReleaseDir=D:/package/as/madq_
	# APP版本号,用来升级使用
	appVersionCode=2
	# APP版本名称,最终打包使用
	appVersionName=1.0.0.2
	# app正式版包名后缀
	appSuffixName=_release.apk

properties文件之后,在gradle中引用的方式如下:

	// 默认版本号
	ext.appVersionCode = 1
	// 默认版本名
	ext.appVersionName = "1.0.0.0"
	// 默认apk输出路径
	ext.appReleaseDir = "D:\\package\\as\\_"
	// 默认正式包后缀名
	ext.appSuffixName = "_release.apk"
	
	// 加载版本信息配置文件方法
	def loadProperties() {
	    def proFile = file("../local.properties")
	    Properties pro = new Properties()
	    proFile.withInputStream { stream->
	        pro.load(stream)
	    }
	    appReleaseDir = pro.appReleaseDir
	    appVersionCode = Integer.valueOf(pro.appVersionCode)
	    appVersionName = pro.appVersionName
	    appSuffixName = pro.appSuffixName
	}
	//加载版本信息
	loadProperties()

上文的代码,实现了从配置文件读取属性,赋值给本地变量,这里chu,注意,本地变量一定要使用ext.xxx进行定义,如果使用def定义,是读取不到外部文件的属性的,运行会报属性找不到的错误;此外,还要注意,定以完方法之后,要调用一次,方法才会执行。加载到属性之后,只需要使用变量值设置版本号等信息就可以了:

	defaultConfig {
    ...
    versionCode appVersionCode
    versionName appVersionName
    ...
   }

这样就可以省去每次改动版本号,都得sync gradle的麻烦了,此外,还可以自己定义一些版本号自动更新策略,例如在某些gradle任务(通常是aR任务)执行成功后,进行版本号+1操作等,这样整个版本管理都轻松不少。

2.自定义apk文件输出路径及apk文件名

使用android studio进行apk打包和使用eclipse不同,eclipse在签名打包的过程中会让你指定文件名和输出路径,但是android studio如果你不进行配置,文件名就是固定的,只会让你指定一个路径,而且为了避免前后打的包命名冲突,每次都得该apk文件名,很麻烦,使用gradle配置就能够解决这个问题。

可以看到,在前文中提到的配置文件中,除了版本信息,还配置了一个apk输出地址appReleaseDir 信息,要实现将apk输出到指定地址,需要如下操作:

	applicationVariants.all { variant ->
            variant.outputs.each { output ->
                //开始输出,自定义输出路径
                output.outputFile = 
                new File(appReleaseDir + getDate() + 
                "_v" + appVersionName + 
                variant.productFlavors[0].name + 
                appSuffixName)
            }
        }

在上面的代码中,组装apk文件名的时候,使用到了一个getDate方法,用于获取格式化时间,避免多次打包命名冲突的问题,此外在命名的时候还使用了variant.productFlavors[0].name,这是多渠道打包,用于标记该包是哪个渠道的,下文会具体说多渠道打包,这里先不用管,指定apk输出路径和文件名就是这么简单。

	def getDate() {
	    def date = new Date()
	    def formattedDate = date.format('yyyy_MM_dd_HHmm')
	    return formattedDate
	}

3.gradle配置多渠道打包

在国内,打包做的最好的莫过于友盟了,本文就介绍使用友盟进行多渠道打包,使用友盟进行多渠道打包,可以实现统计应用在每个渠道市场被下载次数的功能。在gradle进行多渠道打包配置,可以一次性打出每个渠道的包,省的为每个市场一个一个打。试想一下,国内应用市场最少十几二十个,如果手动为每个市场单独打包,程序员非得哭晕在厕所啊。使用gradle配置,可以一个任务打出所有渠道的包,结合上文介绍,可以让所有的release包以指定apk文件名输出到指定路径,想想还是非常爽的,而且结合Jenkins自动构建平台,还可以将打出的包发布到指定服务器,供用户通过连接或者二维码下载,相当方便。通过友盟进行多渠道打包,主要有以下两步:

a.在manifest.xml文件中进行UMENG_CHANNEL的配置

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

b.在gradle中替换manifest.xml中声明的占位符

	// 友盟多渠道打包
productFlavors {
    // 360手机助手
    _360 { }
    // 91手机助手
    _91 {}
    // 应用汇
    _yingyonghui {}
    // 豌豆荚
    _wandoujia { }
    // 百度手机助手
    _baidu { }
    ...
}

productFlavors.all { flavor ->
    flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name]
}

其实对productFlavors 的配置还有另一种方式,即:

productFlavors {
    _wandoujia {
        manifestPlaceholders = [UMENG_CHANNEL_VALUE: "wandoujia"]
    }
    _360 {
        manifestPlaceholders = [UMENG_CHANNEL_VALUE: "_360"]
    }
}

4.gradle构建android用到的所有配置如下:

	apply plugin: 'com.android.application'

	// 默认版本号
	ext.appVersionCode = 1
	// 默认版本名
	ext.appVersionName = "1.0.0.0"
	// 默认apk输出路径
	ext.appReleaseDir = "D:\\package\\as\\mdq_"
	// 默认正式包后缀名
	ext.appSuffixName = "_release.apk"
	
	// 加载版本信息配置文件方法
	def loadProperties() {
	    def proFile = file("../local.properties")
	    Properties pro = new Properties()
	    proFile.withInputStream { stream->
	        pro.load(stream)
	    }
	    appReleaseDir = pro.appReleaseDir
	    appVersionCode = Integer.valueOf(pro.appVersionCode)
	    appVersionName = pro.appVersionName
	    appSuffixName = pro.appSuffixName
	}
	// 加载版本信息
	loadProperties()
	// 应用相关配置
	android {
	    compileSdkVersion 18
	    buildToolsVersion "21.1.2"
	
	    defaultConfig {
	        // 应用id,即包名
	        applicationId "ab.cd"
	        // 最低适配版本,低于此版本的手机无法安装
	        minSdkVersion 16
	        // 目标版本,即在该版本上做了充分测试,应用最适用的版本
	        targetSdkVersion 22
	        // 版本号,每打一次包加1
	        versionCode appVersionCode
	        // 版本名,例如1.0.1,通常用三位,表示主版本号.分版本号.补丁号(小版本号)
	        versionName appVersionName
	        // dex突破65535限制,当app方法数超过限制,会采用多dex打包
	        multiDexEnabled true
	        // 默认打包渠道是友盟
	        manifestPlaceholders = [UMENG_CHANNEL_VALUE: "umeng"]
	    }
	
	    // 禁止Lint出错导致打包异常终止
	    lintOptions {
	        disable 'MissingTranslation', 'ExtraTranslation'
	        abortOnError false
	        ignoreWarnings true
	    }
	
	    //签名信息
	    signingConfigs {
	        // debug签名信息
	        debugConfig {
	            storeFile file("C:\\debug.keystore")
	            storePassword "123456"
	            keyAlias "debug"
	            keyPassword "123456"
	        }
	        // 发布版签名
	        releaseConfig {
	            storeFile file("C:\\release.keystore")
	            storePassword "123456"
	            keyAlias "release"
	            keyPassword "123456"
	        }
	    }
	
	    buildTypes {
	        // debug构建配置
	        debug {
	            // 显示Log
	            buildConfigField "boolean", "LOG_DEBUG", "true"
	            // apk包名称后缀,用来区分release和debug
	            versionNameSuffix "_debug"
	            // 不混淆
	            minifyEnabled false
	            // 不压缩优化
	            zipAlignEnabled false
	            // 不进行资源优化(删除无用资源等)
	            shrinkResources false
	            // 使用的签名信息
	            signingConfig signingConfigs.debugConfig
	        }
	        // release构建配置
	        release {
	            // 正式版不显示log
	            buildConfigField "boolean", "LOG_DEBUG", "false"
	            // 进行混淆
	            minifyEnabled true
	            // 进行压缩优化
	            zipAlignEnabled true
	            // 进行资源优化,移除无用的resource文件
	            shrinkResources true
	            // 使用的签名信息
	            signingConfig signingConfigs.releaseConfig
	            // 使用的混淆规则文件,前面是系统默认的文件,会全部混淆,
	            // 后面是自定义不混淆的文件(domain,android四大组件,自定义view等一般是不能混淆的)
	            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-project.txt'
	            //应用编译完成,自定义apk输出位置及文件名
	            applicationVariants.all { variant ->
	                variant.outputs.each { output ->
	                    //开始输出,自定义输出路径
	                    output.outputFile = new File(appReleaseDir + getDate() + "_v" +
	                            appVersionName + variant.productFlavors[0].name + appSuffixName)
	                }
	            }
	        }
	    }
	    // 打包忽略掉第三方jar相同的文件  打包排除以下文件,屏蔽因as自身bug,在没有重复引用jar时,提示jar重复引用的问题
	    packagingOptions {
	        exclude 'META-INF/DEPENDENCIES.txt'
	        exclude 'META-INF/LICENSE.txt'
	        exclude 'META-INF/NOTICE.txt'
	        exclude 'META-INF/NOTICE'
	        exclude 'META-INF/LICENSE'
	        exclude 'META-INF/DEPENDENCIES'
	        exclude 'META-INF/notice.txt'
	        exclude 'META-INF/license.txt'
	        exclude 'META-INF/dependencies.txt'
	        exclude 'META-INF/LGPL2.1'
	    }
	    // 友盟多渠道打包
	    productFlavors {
	//        使用注释掉的这种方式也可以实现多渠道打包,这样就不用下面的productFlavors.all函数了
	//        如果只使用占位信息定义,如wandoujia{},则需要productFlavors.all函数同意标识
	//        wandoujia {
	//            manifestPlaceholders = [UMENG_CHANNEL_VALUE: "wandoujia"]
	//        }
	        // 360手机助手
	        _360 { }
	        // 91手机助手
	        _91 {}
	        // 应用汇
	        _yingyonghui {}
	        // 豌豆荚
	        _wandoujia { }
	        // 百度手机助手
	        _baidu { }
	        // 安智市场
	        _anzhi { }
	        // 机锋
	        _jifeng { }
	        // 魅族市场
	        _meizu { }
	        // 小米市场
	        _xiaomi { }
	        // google商店
	        _googleplay { }
	        // 安卓市场
	        _anzhuoshichang { }
	        // 华为应用商店
	        _huawei { }
	        // 淘宝手机助手
	        _taobao { }
	    }
	
	    productFlavors.all { flavor ->
	        flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name]
	    }
	}
	
	// 获取格式化时间,用来标识打包时间,同时避免命名冲突
	def getDate() {
	    def date = new Date()
	    def formattedDate = date.format('yyyy_MM_dd_HHmm')
	    return formattedDate
	}
	
	dependencies {
	    compile project(':andBase')
	    compile project(':initActivity')
	    compile files('libs/android-async-http-1.4.4.jar')
	    compile files('libs/baidumapapi_v3_2_0.jar')
	    compile files('libs/easemobchat_2.1.8.jar')
	    compile files('libs/fastjson-1.1.31.jar')
	    compile files('libs/HCNetSDK.jar')
	    compile files('libs/jsr305-2.0.1.jar')
	    compile files('libs/locSDK_3.3.jar')
	    compile files('libs/PlayerSDK.jar')
	    compile files('libs/umeng-analytics-v5.5.3.jar')
	    compile files('libs/universal-image-loader-1.9.3.jar')
	    compile files('libs/xUtils-2.6.14.jar')
	}
	// 使用自己的LockHunter进行文件解锁,
	// as中clean的时候总是提示删不掉这个文件那个文件,这里自己的clean任务就可以删除
	// 前提是安装了lockhunter
	task clean(type: Exec) {
	    ext.lockhunter = "D:\\Program Files\\LockHunter.exe"
	    def buildDir = file("build")
	    commandLine 'cmd', "$lockhunter", '/delete', '/silent', buildDir
	}

5.其它可参考配置博客地址



支付宝打赏 微信打赏

赞赏一下