摘要:上周我們?cè)谙盗腥腴T篇文章中已經(jīng)將在項(xiàng)目中的結(jié)構(gòu)過(guò)了一遍。但是不同的是它可以改變其自身的代理。這正常,因?yàn)槲覀冞€沒(méi)有聲明它。與將通過(guò)對(duì)象進(jìn)行。至于是的中的一個(gè)抽象感念,它申明在中。的知識(shí)點(diǎn)還有很多,這只是對(duì)有關(guān)的一部分進(jìn)行分析。
上周我們?cè)贏ndroid Gradle系列-入門篇文章中已經(jīng)將gradle在項(xiàng)目中的結(jié)構(gòu)過(guò)了一遍。對(duì)于gradle,我們?cè)S多時(shí)候都不需要修改類似與*.gradle文件,做的最多的應(yīng)該是在dependencies中添加第三方依賴,或者說(shuō)修改sdk版本號(hào),亦或者每次發(fā)版本改下versionCode與versionName。即使碰到問(wèn)題也是直接上google尋找答案,而并沒(méi)有真正理解它為什么要這么做,或者它是如何運(yùn)行的?
今天,我會(huì)通過(guò)這篇文章一步一步的編寫gradle文件,從項(xiàng)目的創(chuàng)建,到gradle的配置。相信有了這篇文章,你將對(duì)gradle的內(nèi)部運(yùn)行將有一個(gè)全新的認(rèn)識(shí)。
Groovy在講gradle之前,我們還需明白一點(diǎn),gradle語(yǔ)法是基于groovy的。所以我們先來(lái)了解一些groovy的知識(shí),這有助于我們之后的理解。當(dāng)然如果你已經(jīng)有g(shù)roovy的基礎(chǔ)你可以直接跳過(guò),沒(méi)有的也不用慌,因?yàn)橹灰愣甹ava就不是什么難題。
syntax下面我將通過(guò)code的形式,列出幾點(diǎn)
當(dāng)調(diào)用的方法有參數(shù)時(shí),可以不用(),看下面的例子
def printAge(String name, int age) { print("$name is $age years old") } def printEmptyLine() { println() } def callClosure(Closure closure) { closure() } printAge "John", 24 //輸出John is 24 years old printEmptyLine() //輸出空行 callClosure { println("From closure") } //輸出From closure
如果最后的參數(shù)是閉包,可以將它寫在括號(hào)的外面
def callWithParam(String param, Closureclosure) { closure(param) } callWithParam("param", { println it }) //輸出param callWithParam("param") { println it } //輸出param callWithParam "param", { println it } //輸出param
調(diào)用方法時(shí)可以指定參數(shù)名進(jìn)行傳參,有指定的會(huì)轉(zhuǎn)化到Map對(duì)象中,沒(méi)有的將按正常傳參
def printPersonInfo(Mapperson) { println("${person.name} is ${person.age} years old") } def printJobInfo(Map job, String employeeName) { println("${employeeName} works as ${job.name} at ${job.company}") } printPersonInfo name: "Jake", age: 29 printJobInfo "Payne", name: "Android Engineer", company: "Google"
你會(huì)發(fā)現(xiàn)他們的調(diào)用都不需要括號(hào),同時(shí)printJobInfo的調(diào)用參數(shù)的順序不受影響。
Closure在gradle中你會(huì)發(fā)現(xiàn)許多閉包,所以我們需要對(duì)閉包有一定的了解。如果你熟悉kotlin,它與Function literals with receiver類似。
在groovy中我們可以將Closures當(dāng)做成lambdas,所以它可以直接當(dāng)做代碼塊執(zhí)行,可以有參數(shù),也可以有返回值。但是不同的是它可以改變其自身的代理。例如:
class DelegateOne { def callContent(String content) { println "From delegateOne: $content" } } class DelegateTow { def callContent(String content) { println "From delegateTwo: $content" } } def callClosure = { callContent "I am bird" } callClosure.delegate = new DelegateOne() callClosure() //輸出From delegateOne: I am bird callClosure.delegate = new DelegateTow() callClosure() //輸出From delegateTow: I am bird
通過(guò)改變callClosure的delegate,讓其調(diào)用不同的callContent。
如果你想了解更多,可以直接閱讀groovy文檔
在上篇文章中已經(jīng)提到有關(guān)gradle的腳步相關(guān)的知識(shí),這里就不再累贅。
下面我們來(lái)一步一步構(gòu)建gradle。
首先我們新建一個(gè)文件夾example,cd進(jìn)入該文件夾,在該目錄下執(zhí)行g(shù)radle projects,你會(huì)發(fā)現(xiàn)它已經(jīng)是一個(gè)gradle項(xiàng)目了
$ gradle projects > Task :projects ------------------------------------------------------------ Root project ------------------------------------------------------------ Root project "example" No sub-projects To see a list of the tasks of a project, run gradle:tasks For example, try running gradle :tasks BUILD SUCCESSFUL in 5s
因?yàn)檫@里不是在Android Studio中創(chuàng)建的項(xiàng)目,所以如果你本地沒(méi)有安裝與配置gradle環(huán)境,將不會(huì)有g(shù)radle命令。所以這一點(diǎn)要注意一下。
每一個(gè)android項(xiàng)目在它的root project下都需要配置一個(gè)settings.gradle,它代表著項(xiàng)目的全局配置。同時(shí)使用void include(String[] projectPaths)方法來(lái)添加子項(xiàng)目,例如我們?yōu)閑xample添加app子項(xiàng)目
$ echo "include ":app"" > settings.gradle $ gradle projects > Task :projects ------------------------------------------------------------ Root project ------------------------------------------------------------ Root project "example" --- Project ":app" To see a list of the tasks of a project, run gradle:tasks For example, try running gradle :app:tasks BUILD SUCCESSFUL in 1s 1 actionable task: 1 executed
:app中的:代表的是路徑的分隔符,同時(shí)在settings.gradle中默認(rèn)root project是該文件的文件夾名稱,也可以通過(guò)rootProject.name = name來(lái)進(jìn)行修改。
搭建Android子項(xiàng)目現(xiàn)在需要做的是將子項(xiàng)目app構(gòu)建成Android項(xiàng)目,所以我們需要配置app的build.gradle。因?yàn)間radle只是構(gòu)建工具,它是根據(jù)不同的插件來(lái)構(gòu)建不同的項(xiàng)目,所以為了符合Android的構(gòu)建,需要申明應(yīng)用的插件。這里通過(guò)apply方法,它有以下三種類型
void apply(Closure closure) void apply(Mapoptions) void apply(Action super ObjectConfigurationAction> action)
這里我們使用的是第二種,它的map參數(shù)需要與ObjectConfigurationAction中的方法名相匹配,而它的方法名有以下三種
from: 應(yīng)用一個(gè)腳本文件
plugin: 應(yīng)用一個(gè)插件,通過(guò)id或者class名
to: 應(yīng)用一個(gè)目標(biāo)代理對(duì)象
因?yàn)槲覀円褂胊ndroid插件,所以需要使用apply(plugin: "com.android.application"),又由于groovy的語(yǔ)法特性,可以將括號(hào)省略,所以最終在build.gradle中的表現(xiàn)可以如下:
$ echo "apply plugin: "com.android.application"" > app/build.gradle
添加完以后,再來(lái)執(zhí)行一下
$ gradle app:tasks FAILURE: Build failed with an exception. * Where: Build file "/Users/idisfkj/example/app/build.gradle" line: 1 * What went wrong: A problem occurred evaluating project ":app". > Plugin with id "com.android.application" not found. * Try: Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights. * Get more help at https://help.gradle.org BUILD FAILED in 6s
發(fā)現(xiàn)報(bào)錯(cuò)了,顯示com.android.application的插件id找不到。這正常,因?yàn)槲覀冞€沒(méi)有聲明它。所以下面我們要在project下的build.gradle中聲明它。為什么不直接到app下的build.gradle聲明呢?是因?yàn)槲覀兪莂ndroid項(xiàng)目,project可以有多個(gè)sub-project,所以為了防止在子項(xiàng)目中重復(fù)聲明,統(tǒng)一到主項(xiàng)目中聲明。
project的build.gradle聲明插件需要在buildscript中,而buildscript會(huì)通過(guò)ScriptHandler來(lái)執(zhí)行,以至于sub-project也能夠使用。所以最終的申明如下:
buildscript { repositories { google() jcenter() } dependencies { classpath "com.android.tools.build:gradle:3.3.2" } }
上面的buildscript、repositories與dependencies方法都是以Closure作為參數(shù),然后再通過(guò)delegate進(jìn)行調(diào)用
buildscript(Closure)在Project中調(diào)用,通過(guò)ScriptHandler來(lái)執(zhí)行Closure
repositories(Closure)在ScriptHandler中調(diào)用,通過(guò)RepositoryHandler來(lái)執(zhí)行Closure
dependencies(Closure)在ScriptHandler中調(diào)用,通過(guò)DependencyHandler來(lái)執(zhí)行Closure
相應(yīng)的google()與jcenter()會(huì)在RepositoryHandler執(zhí)行,classpaht(String)會(huì)在DependencyHandler(*)執(zhí)行。
如果你想更詳細(xì)的了解可以查看文檔
讓我們?cè)僖淮螆?zhí)行g(shù)radle projects
$ gradle projects FAILURE: Build failed with an exception. * What went wrong: A problem occurred configuring project ":app". > compileSdkVersion is not specified. * Try: Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights. * Get more help at https://help.gradle.org BUILD FAILED in 1s
發(fā)現(xiàn)報(bào)沒(méi)有指定compileSdkVersion,因?yàn)槲覀冞€沒(méi)有對(duì)app進(jìn)行相關(guān)的配置,只是引用了android插件。所以我們現(xiàn)在來(lái)進(jìn)行基本配置,在app/build.gradle中添加
android { buildToolsVersion "28.0.1" compileSdkVersion 28 }
我們?cè)赼ndroid中進(jìn)行聲明,android方法會(huì)加入到project實(shí)例中。buildToolsVersion與compileSdkVersion將通過(guò)Closure對(duì)象進(jìn)行delegate。
Extensionsandroid方法會(huì)是如何與project進(jìn)行關(guān)聯(lián)的?在我們聲明的Android插件中,會(huì)注冊(cè)一個(gè)AppExtension類,這個(gè)extension將會(huì)與android命名。所以gradle能夠調(diào)用android方法,而在AppExtension中已經(jīng)聲明了各種方法屬性,例如buildTypes、defaultConfig與signingConfigs等。這也就是為什么我們能夠在android方法中調(diào)用它們的原因。下面是extension的創(chuàng)建部分源碼
@Override void apply(Project project) { super.apply(project) // This is for testing. if (pluginHolder != null) { pluginHolder.plugin = this; } def buildTypeContainer = project.container(DefaultBuildType, new BuildTypeFactory(instantiator, project.fileResolver)) def productFlavorContainer = project.container(GroupableProductFlavorDsl, new GroupableProductFlavorFactory(instantiator, project.fileResolver)) def signingConfigContainer = project.container(SigningConfig, new SigningConfigFactory(instantiator)) extension = project.extensions.create("android", AppExtension, this, (ProjectInternal) project, instantiator, buildTypeContainer, productFlavorContainer, signingConfigContainer) setBaseExtension(extension) ... }Dependencies
android方法下面就是dependencies,下面我們?cè)賮?lái)看dependencies
dependencies { implementation "io.reactivex.rxjava2:rxjava:2.0.4" testImplementation "junit:junit:4.12" annotationProcessor "org.parceler:parceler:1.1.6" }
有了上面的基礎(chǔ),應(yīng)該會(huì)容易理解。dependencies是會(huì)被delegate給DependencyHandler,不過(guò)如果你到DependencyHandler中去查找,會(huì)發(fā)現(xiàn)找不到上面的implementation、testImplementation等方法。那它們有到底是怎么來(lái)的呢?亦或者如果我們添加了dev flavor,那么我又可以使用devImplementation。這里就涉及到了groovy的methodMissing方法。它能夠在runtime(*)中捕獲到?jīng)]有定義的方法。
至于(*)是gradle的methodMissing中的一個(gè)抽象感念,它申明在MethodMixIn中。
對(duì)于DependencyHandler的實(shí)現(xiàn)規(guī)則是:
在DependencyHandler中如果我們回調(diào)了一個(gè)沒(méi)有定義的方法,且它有相應(yīng)的參數(shù);同時(shí)它的方法名在configuration(*)中;那么將會(huì)根據(jù)方法名與參數(shù)類型來(lái)調(diào)用doAdd的相應(yīng)方法。
對(duì)于configuration(*),每一個(gè)plugin都有他們自己的配置,例如java插件定義了compile、compileClassPath、testCompile等。而對(duì)于Android插件在這基礎(chǔ)上還會(huì)定義annotationProcessor,(variant)Implementation、(variant)TestImplementation等。對(duì)于variant則是基于你設(shè)置的buildTypes與flavors。
另一方面,由于doAdd()是私用的方法,但add()是公用的方法,所以在dependencies中我們可以直接使用add
dependencies { add("implementation", "io.reactivex.rxjava2:rxjava:2.0.4") add("testImplementation", "junit:junit:4.12") add("annotationProcessor", "org.parceler:parceler:1.1.6") }
注意,這種寫法并不推薦,這里只是為了更好的理解它的原理。
gradle的知識(shí)點(diǎn)還有很多,這只是對(duì)有關(guān)Android的一部分進(jìn)行分析。當(dāng)我們進(jìn)行g(shù)radle配置的時(shí),不至于對(duì)gradle的語(yǔ)法感到魔幻,或者對(duì)它的一些操作感到不解。
我在github上建了一個(gè)倉(cāng)庫(kù)Android精華錄,收集Android相關(guān)的文章,如果有需要的可以去看一下,有好的文章可以加我微信fan331100推薦給我。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/74516.html
摘要:全稱應(yīng)用性能管理監(jiān)控后面我會(huì)通過(guò)一系列的文章來(lái)介紹的原理框架設(shè)計(jì)與實(shí)現(xiàn)等等。在應(yīng)用構(gòu)建期間,通過(guò)修改字節(jié)碼的方式來(lái)進(jìn)行字節(jié)碼插樁就是實(shí)現(xiàn)自動(dòng)化的方案之一。 showImg(https://segmentfault.com/img/bVbbRX6?w=1995&h=1273); 歡迎關(guān)注微信公眾號(hào):BaronTalk,獲取更多精彩好文! 一. 前言 性能問(wèn)題是導(dǎo)致 App 用戶流失的罪魁...
摘要:如果你有新建一個(gè)項(xiàng)目的經(jīng)歷,那么你將看到推薦的方案在的中使用來(lái)定義版本號(hào)全局變量。例如之前的版本號(hào)就可以使用如下方式實(shí)現(xiàn)因?yàn)槭褂玫氖钦Z(yǔ)言,所以以上都是語(yǔ)法例如版本控制,上面代碼的意思就是將有個(gè)相關(guān)的版本依賴放到的變量中,同時(shí)放到了中。 showImg(https://segmentfault.com/img/bVbsh3m?w=2560&h=1280); 上篇文章我們已經(jīng)將Gradle...
閱讀 3631·2021-11-22 09:34
閱讀 3198·2021-11-15 11:38
閱讀 3079·2021-10-27 14:16
閱讀 1264·2021-10-18 13:35
閱讀 2438·2021-09-30 09:48
閱讀 3440·2021-09-29 09:34
閱讀 1663·2019-08-30 15:54
閱讀 1830·2019-08-26 11:57