大部分人都经历过高中,不难发现高考650分的人和450分的人书单基本上是一样的,这是为什么呢?
这往往并不是因为他们接触了更多的信息,而是因为他们处理信息的方式与众不同。他们往往善于整理信息,并且获得“系统化知识体系”。
写在前面的话
今天翻了翻自己之前的博客,发现上一篇还是在2017-01-12发的。不知不觉3个月就过去了,之间好像再也没有很系统的总结过东西了。仔细想想一年也没几个3个月,还是有点方的。
3个月说长不长,但也足够发生很多事情。比如我投身到了直播(YOLO)行业,开始接手前人(Piasy、promeG等等)的项目,之间曲折自不需多说。好在已经适应了现在的节奏,博客也会慢慢更新。
其实直播并没有想象中的那样复杂,概括的讲无非就是采集端(推流端)和观众端(拉流端)。然后再添加一些礼物系统、弹幕系统、聊天系统、人脸识别、游戏互动、连麦互动、美颜等功能使之丰满。
关于直播,我们暂且不进行深入讨论。今天我们来说一说Gradle相关的东西。可能好多人会有这样的感慨,为什么我看了那么多“Gradle从入门到精通”,仍然管理不好自己项目的构建呢?今天我们就来一起整理下Gradle相关的那些知识点,也许会有收获哦。
Gradle思维导图
为了方便梳理Gradle的知识点,我画了下面的思维导图,有不足的地方还望大家指出。其实这些点基本上每个都可以写一篇单独的博客来扩展,而且有很多已经总结的很好了,这里就不赘述了。
而关于我们为什么要用Gradle,Gradle能做什么这样的问题,弄清楚也是很有必要的。比较官方的说法是:Gradle使用易懂的DSL语法 ,将开发过程中需要的编译、构建、测试、打包以及部署工作,变得非常简单、而且方便重复使用。即Gradle可以从开发过程的各个环节来简化我们的开发。
Gradle实战
俗话说的好:Talk is cheap,show me the code。接下来我就抛砖引玉,分享下我们的Gradle是怎样配置的。
项目根目录下的build.gradle文件
// Top-level build file where you can add configuration options common to all sub-projects/modules.
apply from: 'buildsystem/dependencies.gradle'
buildscript {
repositories {
jcenter()
}
dependencies {
apply from: 'buildsystem/dependencies.gradle'
classpath "com.android.tools.build:gradle:$gradleAndroidVersion"
classpath 'com.github.promeg:android-multi-channel-plugin:0.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
app下的build.gradle文件
import java.text.SimpleDateFormat
import static org.apache.tools.ant.taskdefs.condition.Os.FAMILY_WINDOWS
import static org.apache.tools.ant.taskdefs.condition.Os.isFamily
if (!testDevelopURC) {
apply plugin: 'com.android.library'
} else {
apply plugin: 'com.android.application'
}
apply plugin: 'android-multi-channel'
def keyConfigPath
if (isFamily(FAMILY_WINDOWS)) {
keyConfigPath = System.getenv('USERPROFILE') + File.separator + ".ssh" + File.separator +
"androidKeystore.properties"
} else {
keyConfigPath = System.getenv('HOME') + "/.ssh/androidKeystore.properties"
}
Properties props = new Properties()
if (new File(keyConfigPath).exists()) {
props.load(new FileInputStream(file(keyConfigPath)))
}
android {
def rootDep = rootProject.ext
compileSdkVersion rootDep.androidCompileSdkVersion
buildToolsVersion rootDep.androidBuildToolsVersion
defaultConfig {
minSdkVersion rootDep.androidMinSdkVersionn
targetSdkVersion rootDep.androidTargetSdkVersion
versionCode rootDep.releaseVersionCode
versionName rootDep.releaseVersionName
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
multiDexEnabled true
vectorDrawables.useSupportLibrary = true
if (!testDevelopURC) {
applicationId "tv.yoloyolo.renlei.gradlepractice"
} else {
consumerProguardFiles 'proguard-rules.pro', 'proguard-fresco.pro'
}
buildConfigField "String", "API_BASE_URL", "\"https://api.test.base\""
manifestPlaceholders = [EASEMOB_APPKEY: "publish#publish"]
}
lintOptions {
abortOnError false
}
packagingOptions {
exclude 'META-INF/LICENSE.txt'
exclude 'LICENSE.txt'
exclude 'META-INF/LICENSE'
exclude 'META-INF/NOTICE'
exclude 'META-INF/NOTICE.txt'
exclude 'META-INF/services/javax.annotation.processing.Processor'
exclude 'META-INF/rxjava.properties'
}
signingConfigs {
release {
storeFile file(props['keystore'])
storePassword props['keystore.password']
keyAlias "promegu"
keyPassword props['key.password']
}
}
dexOptions {
maxProcessCount 8
javaMaxHeapSize "6g"
}
testOptions.unitTests.all {
testLogging {
events 'passed', 'skipped', 'failed', 'standardOut', 'standardError'
outputs.upToDateWhen { false }
showStandardStreams = true
}
// configure the test JVM arguments
jvmArgs '-noverify'
}
aaptOptions {
cruncherEnabled false
}
publishNonDefault true
productFlavors {
dev {
ndk {
abiFilter "armeabi"
}
buildConfigField "String", "API_BASE_URL", "\"https://api.test.base\""
manifestPlaceholders = [EASEMOB_APPKEY: "test#test"]
}
production {
ndk {
abiFilter "armeabi"
}
// inherit from default config
}
}
buildTypes {
debug {
minifyEnabled false
debuggable true
ext.enableCrashlytics = false
signingConfig signingConfigs.release
}
release {
minifyEnabled false
debuggable false
signingConfig signingConfigs.release
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
multiFlavors {
prefix = "TEST_";
def released = new SimpleDateFormat('yyyy-MM-dd').format(new Date())
subfix = "_$released";
defaultSigningConfig = android.signingConfigs.release
channelConfig {
production {
childFlavors =
["Tencent", "360", "Baidu", "Alibaba", "GFan", "Sogou",
"Lenovo", "XiaoMi", "Meizu", "OPPO", "Huawei", "GooglePlay"]
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:25.3.1'
testCompile 'junit:junit:4.12'
testCompile 'org.robolectric:shadows-support-v4:' + rootProject.ext.robolectricVersion
compile('com.github.promeg:android-multi-channel-plugin-lib:0.1') {
exclude module: 'appcompat-v7'
}
}
外部的dependencies.gradle文件
我在项目根目录创建了一个叫buildsystem的文件夹,dependencies.gradle文件放到了这个文件夹下。
allprojects {
repositories {
jcenter()
}
}
ext {
androidBuildToolsVersion = '25.0.2'
androidCompileSdkVersion = 25
androidMinSdkVersionn = 15
androidTargetSdkVersion = 25
androidSupportSdkVersion = '25.1.0'
gradleAndroidVersion = '2.2.3'
releaseVersionCode = 1
releaseVersionName = "1.0"
robolectricVersion = '3.0'
}
项目根目录下的gradle.properties文件
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
#org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
org.gradle.jvmargs=-Xmx8192m -XX\:MaxPermSize\=3072m
org.gradle.daemon=true
org.gradle.configureondemand=true
org.gradle.parallel=true
testDevelopURC=false
应用到的知识点
基本上就是这样了,这几个文件的配置是从YOLO项目简化而来的,不过不影响研究。接下来我们看下里边用到的点。由于具体每个点的实现网上好多已经介绍的很全面了,这里我只说下大概的点,具体大家可以自行google。后边我也会附上一些链接供大家参考。
依赖外部配置
如果我们的配置文件过大或者有需要复用的配置,可以考虑抽离成一个单独的文件,引入方式如下:
apply from: 'buildsystem/dependencies.gradle'
全局配置
Stack Overflow:How to define common android properties for all modules using gradle
ext {
androidBuildToolsVersion = '25.0.2'
androidCompileSdkVersion = 25
androidMinSdkVersionn = 15
androidTargetSdkVersion = 25
androidSupportSdkVersion = '25.1.0'
gradleAndroidVersion = '2.2.3'
releaseVersionCode = 1
releaseVersionName = "1.0"
robolectricVersion = '3.0'
}
占位符
如果我们在manifest中定义了占位符的话,可以在module的build.gradle
中可以将其进行赋值。
<meta-data
android:name="EASEMOB_APPKEY"
android:value="${EASEMOB_APPKEY}" />
manifestPlaceholders = [EASEMOB_APPKEY: "publish#publish"]
ProductFlavors
对于ProductFlavors,我们可以在Build->Select Build Variant…选择我们开发状态下使用的默认配置。
productFlavors {
dev {
ndk {
abiFilter "armeabi"
}
buildConfigField "String", "API_BASE_URL", "\"https://api.test.base\""
manifestPlaceholders = [EASEMOB_APPKEY: "test#test"]
}
production {
ndk {
abiFilter "armeabi"
}
// inherit from default config
}
}
BuildTypes
Gradle官方文档:BuildType,这篇讲的是有BuildType有哪些可以配置东西。
debug {
minifyEnabled false
debuggable true
ext.enableCrashlytics = false
signingConfig signingConfigs.release
}
BuildConfig
buildConfigField "String", "API_BASE_URL", "\"https://api.test.base\""
dependencies
包括依赖的几种类型,以及如果解决依赖冲突。
Gradle官方文档:依赖项管理基础知识
Android官方文档:Add Build Dependencies
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:25.3.1'
testCompile 'junit:junit:4.12'
testCompile 'org.robolectric:shadows-support-v4:' + rootProject.ext.robolectricVersion
compile('com.github.promeg:android-multi-channel-plugin-lib:0.1') {
exclude module: 'appcompat-v7'
}
}
签名
这里用到了一些Groovy中的语法,主要用来找到自己的androidKeystore.properties文件并从中读取配置。
Android官方文档:签署您的应用
def keyConfigPath
if (isFamily(FAMILY_WINDOWS)) {
keyConfigPath = System.getenv('USERPROFILE') + File.separator + ".ssh" + File.separator +
"androidKeystore.properties"
} else {
keyConfigPath = System.getenv('HOME') + "/.ssh/androidKeystore.properties"
}
Properties props = new Properties()
if (new File(keyConfigPath).exists()) {
props.load(new FileInputStream(file(keyConfigPath)))
}
signingConfigs {
release {
storeFile file(props['keystore'])
storePassword props['keystore.password']
keyAlias "promegu"
keyPassword props['key.password']
}
}
各种options配置
Gradle官方文档:Android Plugin DSL Reference
packagingOptions {
exclude 'META-INF/LICENSE.txt'
exclude 'LICENSE.txt'
exclude 'META-INF/LICENSE'
exclude 'META-INF/NOTICE'
exclude 'META-INF/NOTICE.txt'
exclude 'META-INF/services/javax.annotation.processing.Processor'
exclude 'META-INF/rxjava.properties'
}
dexOptions {
maxProcessCount 8
javaMaxHeapSize "6g"
}
testOptions.unitTests.all {
testLogging {
events 'passed', 'skipped', 'failed', 'standardOut', 'standardError'
outputs.upToDateWhen { false }
showStandardStreams = true
}
// configure the test JVM arguments
jvmArgs '-noverify'
}
aaptOptions {
cruncherEnabled false
}
多渠道打包
这个多渠道打包其实是之前同事自己写了一个Gradle插件。传送门。
当然如果你熟悉Groovy语法,也可以自己编写。编写Gradle插件。
multiFlavors {
prefix = "TEST_";
def released = new SimpleDateFormat('yyyy-MM-dd').format(new Date())
subfix = "_$released";
defaultSigningConfig = android.signingConfigs.release
channelConfig {
production {
childFlavors =
["Tencent", "360", "Baidu", "Alibaba", "GFan", "Sogou",
"Lenovo", "XiaoMi", "Meizu", "OPPO", "Huawei", "GooglePlay"]
}
}
}
加速编译
Android官方文档:优化你的编译速度
dexOptions {
maxProcessCount 8
javaMaxHeapSize "6g"
}
org.gradle.jvmargs=-Xmx8192m -XX\:MaxPermSize\=3072m
org.gradle.daemon=true
org.gradle.configureondemand=true
org.gradle.parallel=true
Other
除了上边提到的那些点之外,还有一些总结的比较好的链接。其实国内好多博客都大同小异,都参考自官方文档,所以推荐直接看官方文档。
- 官方文档:Gradle Plugin User Guide
- Gradle Plugin User Guide 翻译版
- 官方文档:构建和运行您的应用
- 官方文档:配置构建概览
- 官方文档:配置构建变体
- 官方文档:从命令行运行Gradle
- 官方文档:设置应用的ID
- Gradle构建最佳实践
- Gradle配置最佳实践
结语
可能上边说的内容比较多,不过大家可以按需查看。而且工欲善其事,必先利其器。对于我们来说,学好Gradle无疑可以对开发提供很大的帮助,节省更多的时间。
感谢官方文档详细的教程,也感谢喜欢分享的那些朋友,如果上边链接有侵权的话,请告诉我,我会删掉。