成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Android Gradle系列-原理篇

jsliang / 395人閱讀

摘要:上周我們?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, Closure closure) {
    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(Map person) {
    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文檔

Gradle

在上篇文章中已經(jīng)提到有關(guān)gradle的腳步相關(guān)的知識(shí),這里就不再累贅。
下面我們來(lái)一步一步構(gòu)建gradle。

搭建項(xiàng)目層級(jí)

首先我們新建一個(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(Map options)
void apply(Action 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。

Extensions

android方法會(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

相關(guān)文章

  • Android 性能監(jiān)控系列一(原理

    摘要:全稱應(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 用戶流失的罪魁...

    yacheng 評(píng)論0 收藏0
  • Android Gradle系列-進(jìn)階

    摘要:如果你有新建一個(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...

    lvzishen 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<