Gradle 常用配置总结
这里分享下我在日常开发中对 gradle 的常用配置规则
一、版本号配置
当项目逐渐演进的过程中,主工程依赖的 module
可能会越来越多,此时就需要统一配置各个 module
的编译参数了
在工程的根目录下新建一个 gradle
文件,命名为 config.gradle
,在此文件中统一声明工程的编译属性和依赖库的版本号
ext { compilesdkversion = 28 minsdkversion = 15 targetsdkversion = 28 versioncode = 1 versionname = '1.0' dependencies = [ appcompatv7 : 'com.android.support:appcompat-v7:28.0.0-rc02', constraintlayout: 'com.android.support.constraint:constraint-layout:1.1.3', junit : 'junit:junit:4.12', testrunner : 'com.android.support.test:runner:1.0.2', espressocore : 'com.android.support.test.espresso:espresso-core:3.0.2' ] }
默认情况下,app module
的 build.gradle
文件的默认配置如下所示
apply plugin: 'com.android.application' android { compilesdkversion 28 defaultconfig { applicationid "leavesc.hello.gradlesamples" minsdkversion 15 targetsdkversion 28 versioncode 1 versionname "1.0" testinstrumentationrunner "android.support.test.runner.androidjunitrunner" } buildtypes { release { minifyenabled false proguardfiles getdefaultproguardfile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { implementation filetree(dir: 'libs', include: ['*.jar']) implementation 'com.android.support:appcompat-v7:28.0.0-rc02' implementation 'com.android.support.constraint:constraint-layout:1.1.3' testimplementation 'junit:junit:4.12' androidtestimplementation 'com.android.support.test:runner:1.0.2' androidtestimplementation 'com.android.support.test.espresso:espresso-core:3.0.2' }
这里将其改为引用 config.gradle
文件的形式
首先,需要在根目录下的 build.gradle
文件中应用 config.gradle
文件,这样在 module
配置文件中才引用得到当中的属性值
此时就可以修改应用版本号以及依赖库的声明方式了
apply plugin: 'com.android.application' def globalconfiguration = rootproject.ext def presentationdependencies = globalconfiguration.dependencies android { compilesdkversion globalconfiguration["compilesdkversion"] defaultconfig { applicationid "leavesc.hello.gradlesamples" minsdkversion globalconfiguration["minsdkversion"] targetsdkversion globalconfiguration["targetsdkversion"] versioncode globalconfiguration["versioncode"] versionname globalconfiguration["versionname"] testinstrumentationrunner "android.support.test.runner.androidjunitrunner" } buildtypes { release { minifyenabled false proguardfiles getdefaultproguardfile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { implementation filetree(include: ['*.jar'], dir: 'libs') implementation presentationdependencies.appcompatv7 implementation presentationdependencies.constraintlayout testimplementation presentationdependencies.junit androidtestimplementation presentationdependencies.testrunner androidtestimplementation presentationdependencies.espressocore }
这样,即使以后工程中包含多个 module
,只要配置的属性都是来自于 config.gradle
文件,就可以做到统一修改编译属性与依赖库版本了
二、签名属性配置
通常,应用的签名类型会分为 release
和 debug
两类,并分别使用不同的签名文件
为了安全考虑以及实现自动化打包,可以通过 gradle
来声明签名配置,包括签名文件路径、签名别名、签名密码等
在 local.properties
文件中声明签名文件路径以及签名密码
sdk.dir=c\:\\software\\sdk key.keystorepath=..\\doc\\key.jks key.keyalias=leavesc key.keypassword=987654321 key.storepassword=123456789
根据配置可知,签名文件是放在工程的 doc
文件夹内
通过代码获取到签名的各个配置项
properties properties = new properties() properties.load(project.rootproject.file('local.properties').newdatainputstream()) def keystorepath_ = properties.getproperty("key.keystorepath") def storepassword_ = properties.getproperty("key.storepassword") def keyalias_ = properties.getproperty("key.keyalias") def keypassword_ = properties.getproperty("key.keypassword") def storefile_ = file(keystorepath_)
配置不同的签名属性以及 build
类型
signingconfigs { release { storefile storefile_ storepassword storepassword_ keyalias keyalias_ keypassword keypassword_ v1signingenabled true v2signingenabled true } debug { storefile storefile_ storepassword storepassword_ keyalias keyalias_ keypassword keypassword_ v1signingenabled true v2signingenabled true } } buildtypes { debug { minifyenabled false proguardfiles getdefaultproguardfile('proguard-android.txt'), 'proguard-rules.pro' signingconfig signingconfigs.debug } release { minifyenabled true proguardfiles getdefaultproguardfile('proguard-android.txt'), 'proguard-rules.pro' signingconfig signingconfigs.release } }
此处,我配置了两种不同的 buildtype
:debug
、release
,并对应不同的签名文件
以后只要选定不同的 build variant
,即可打包具体签名的 apk 文件
而 local.properties
文件可以保存到服务器来实现远程打包,从而保证了隐私安全
三、多渠道打包
有时候,为了方便进行精准营销,会有生成不同渠道包的要求,此时就需要在同个应用上打上不同的渠道id(channelid),这可以通过 productflavors
来实现
先在 androidmanifest.xml
文件中配置占位符,appkey
即对应各个渠道的 id 值
<meta-data android:name="app_key" android:value="${appkey}" />
在 gradle.properties
文件中声明需要的 channelid
以及对应的 applicationid
,在此文件中声明的属性可以直接在 build.gradle
中直接获取到
#默认配置 defaultapplicationid=leavesc.hello.gradlesamples ##各个渠道的配置 #应用宝 yingyongbaochannelid="yingyongbao" yingyongbaoapplicationid=leavesc.hello.gradlesamples.yingyongbao yingyongbaoappkey=appkey_yingyongbao #豌豆荚 wandoujiachannelid="wandoujia" wandoujiaapplicationid=leavesc.hello.gradlesamples.wandoujia wandoujiaappkey=appkey_wandoujia #小米 xiaomichannelid="xiaomi" xiaomiapplicationid=leavesc.hello.gradlesamples.xiaomi xiaomiappkey=appkey_xiaomi
productflavors
可以理解为是对同个产品的不同“风味要求”,可以根据配置项生成特定风味的产品(app)
例如,此处就为不同渠道设定了不同的 applicationid
buildconfigfield
属性则用于在 buildconfig.java
文件中生成特定类型的字段,此处就生成了一个类型为 string
,名为 channelid
的字段,用于方便在应用运行过程中判断当前应用的渠道类型
manifestplaceholders
就是用于替换 androidmanifest.xml
文件中的指定占位符了
productflavors { yingyongbao { applicationid yingyongbaoapplicationid buildconfigfield "string", "channelid", yingyongbaochannelid manifestplaceholders = [appkey: yingyongbaoappkey] } wandoujia { applicationid wandoujiaapplicationid buildconfigfield "string", "channelid", wandoujiachannelid manifestplaceholders = [appkey: wandoujiaappkey] } xiaomi { applicationid xiaomiapplicationid buildconfigfield "string", "channelid", xiaomichannelid manifestplaceholders = [appkey: xiaomiappkey] } }
在主布局文件中展示当前应用的各项属性值
@override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_main); stringbuilder sb = new stringbuilder(); sb.append("applicationid: "); sb.append(getapplicationinfo().packagename); sb.append("\n"); sb.append("applicationname: "); sb.append(getstring(getapplicationinfo().labelres)); sb.append("\n"); sb.append("channelid: "); sb.append(buildconfig.channelid); sb.append("\n"); try { applicationinfo appinfo = getpackagemanager().getapplicationinfo(getpackagename(), packagemanager.get_meta_data); string appkey = appinfo.metadata.getstring("app_key"); sb.append("appkey: "); sb.append(appkey); } catch (packagemanager.namenotfoundexception e) { e.printstacktrace(); } textview tv_appinfo = findviewbyid(r.id.tv_appinfo); tv_appinfo.settext(sb); imageview iv_log = findviewbyid(r.id.iv_log); iv_log.setimageresource(getapplicationinfo().icon); }
四、打包时指定 apk 名字
为了方便标识各个测试包的版本已经打包时间,可以通过 gradle
来指定生成的 apk 文件的命名规则
例如,以下配置就根据 buildtype、flavorname
和 编译时间 来命名 apk 文件
applicationvariants.all { variant -> def buildtype = variant.buildtype.name def flavorname = variant.flavorname def createtime = new date().format("yyyy-mm-dd_hh_mm_ss", timezone.gettimezone("gmt+08:00")) variant.outputs.all { outputfilename = flavorname + "_" + buildtype + "_v" + defaultconfig.versionname + "_" + createtime + ".apk" } }
五、生成属性字段与资源文件值
上边讲过,buildconfigfield
属性可用于在 buildconfig.java
文件中生成特定类型的字段,此处可以利用其来记录应用的编译时间
此外,也可以利用 resvalue
来生成一个 id 引用类型的 string
字符串
首先,声明两个方法,分别用于获取当前时间以及当前电脑的用户信息
static def buildtime() { return new date().format("yyyy-mm-dd hh:mm:ss") } static def hostname() { return system.getproperty("user.name") + "@" + inetaddress.localhost.hostname }
defaultconfig { applicationid defaultapplicationid minsdkversion globalconfiguration["minsdkversion"] targetsdkversion globalconfiguration["targetsdkversion"] versioncode globalconfiguration["versioncode"] versionname globalconfiguration["versionname"] testinstrumentationrunner "android.support.test.runner.androidjunitrunner" flavordimensions '1' resvalue "string", "build_host", hostname() buildconfigfield "string", "build_time", "\"" + buildtime() + "\"" }
用代码来获取这两个属性值
@override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_main); stringbuilder sb = new stringbuilder(); sb.append("applicationid: "); sb.append(getapplicationinfo().packagename); sb.append("\n"); sb.append("applicationname: "); sb.append(getstring(getapplicationinfo().labelres)); sb.append("\n"); sb.append("channelid: "); sb.append(buildconfig.channelid); sb.append("\n"); sb.append("buildtime: "); sb.append(buildconfig.build_time); sb.append("\n"); sb.append("builduser: "); sb.append(getstring(r.string.build_host)); sb.append("\n"); try { applicationinfo appinfo = getpackagemanager().getapplicationinfo(getpackagename(), packagemanager.get_meta_data); string appkey = appinfo.metadata.getstring("app_key"); sb.append("appkey: "); sb.append(appkey); } catch (packagemanager.namenotfoundexception e) { e.printstacktrace(); } textview tv_appinfo = findviewbyid(r.id.tv_appinfo); tv_appinfo.settext(sb); imageview iv_log = findviewbyid(r.id.iv_log); iv_log.setimageresource(getapplicationinfo().icon); }
六、替换资源文件
在多渠道打包时,除了需要在应用中打上特定的标签外,也可能需要使之使用不同的资源文件,例如应用图标和应用名称
此时可以以各个 productflavor
的名称来命名相应的文件夹,并在其中放置相应的图标文件以及声明了应用名称的 string.xml
文件,这样在多渠道打包时,gradle 就会自动引用相应的资源文件
上述所有的示例代码可以在这里获取:gradlesamples
更多的读书笔记可以看这里:一份关于 java 、kotlin 、 android 的学习笔记
上一篇: 算法:最小生成树模板(Prim算法+Kruskal算法)
下一篇: oracle修改用户密码的方法