欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Gradle 依賴切換源碼實(shí)踐示例詳解

 更新時(shí)間:2022年12月09日 10:48:23   作者:開(kāi)發(fā)者如是說(shuō)  
這篇文章主要為大家介紹了Gradle 依賴切換源碼實(shí)踐示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

引言

最近,因?yàn)殚_(kāi)發(fā)的時(shí)候經(jīng)改動(dòng)依賴的庫(kù),所以,我想對(duì) Gradle 腳本做一個(gè)調(diào)整,用來(lái)動(dòng)態(tài)地將依賴替換為源碼。這里以 android-mvvm-and-architecture 這個(gè)工程為例。該工程以依賴的形式引用了我的另一個(gè)工程 AndroidUtils。在之前,當(dāng)我需要對(duì) AndroidUtils 這個(gè)工程源碼進(jìn)行調(diào)整時(shí),一般來(lái)說(shuō)有兩種解決辦法。

1、一般的修改辦法

一種方式是,直接修改 AndroidUtils 這個(gè)項(xiàng)目的源碼,然后將其發(fā)布到 MavenCentral. 等它在 MavenCentral 中生效之后,再將項(xiàng)目中的依賴替換為最新的依賴。這種方式可行,但是修改的周期太長(zhǎng)。

另外一種方式是,修改 Gradle 腳本,手動(dòng)地將依賴替換為源碼依賴。此時(shí),需要做幾處修改,

修改 1,在 settings.gradle 里面將源碼作為子工程添加到項(xiàng)目中,

include ':utils-core', ':utils-ktx'
project(':utils-core').projectDir = new File('../AndroidUtils/utils')
project(':utils-ktx').projectDir = new File('../AndroidUtils/utils-ktx')

修改 2,將依賴替換為工程引用,

// implementation "com.github.Shouheng88:utils-core:$androidUtilsVersion"
// implementation "com.github.Shouheng88:utils-ktx:$androidUtilsVersion"
// 上面的依賴替換為下面的工程引用
implementation project(":utils-core")
implementation project(":utils-ktx")

這種方式亦可行,只不過(guò)過(guò)于繁瑣,需要手動(dòng)修改 Gradle 的構(gòu)建腳本。

2、通過(guò) Gradle 腳本動(dòng)態(tài)修改依賴

其實(shí) Gradle 是支持動(dòng)態(tài)修改項(xiàng)目中的依賴的。動(dòng)態(tài)修改依賴在上述場(chǎng)景,特別是組件化的場(chǎng)景中非常有效。這里我參考了公司組件化的切換源碼的實(shí)現(xiàn)方式,用了 90 行左右的代碼就實(shí)現(xiàn)了上述需求。

2.1 配置文件和工作流程抽象

這種實(shí)現(xiàn)方式里比較重要的一環(huán)是對(duì)切換源碼工作機(jī)制的抽象。這里我重新定義了一個(gè) json 配置文件,

[
  {
    "name": "AndroidUtils",
    "url": "git@github.com:Shouheng88/AndroidUtils.git",
    "branch": "feature-2.8.0",
    "group": "com.github.Shouheng88",
    "open": true,
    "children": [
      {
        "name": "utils-core",
        "path": "AndroidUtils/utils"
      },
      {
        "name": "utils-ktx",
        "path": "AndroidUtils/utils-ktx"
      }
    ]
  }
]

它內(nèi)部的參數(shù)的含義分別是,

  • name:工程的名稱,對(duì)應(yīng)于 Github 的項(xiàng)目名,用于尋找克隆到本地的代碼源碼
  • url:遠(yuǎn)程倉(cāng)庫(kù)的地址
  • branch:要啟用的遠(yuǎn)程倉(cāng)庫(kù)的分支,這里我強(qiáng)制自動(dòng)切換分支時(shí)的本地分支和遠(yuǎn)程分支同名
  • group:依賴的 group id
  • open:表示是否啟用源碼依賴
  • children.name:表示子工程的 module 名稱,對(duì)應(yīng)于依賴中的 artifact id
  • children.path:表示子工程對(duì)應(yīng)的相對(duì)目錄

也就是說(shuō),

  • 一個(gè)工程下的多個(gè)子工程的 group id 必須相同
  • children.name 必須和依賴的 artifact id 相同

上述配置文件的工作流程是,

def sourceSwitches = new HashMap<String, SourceSwitch>()
// Load sources configurations.
parseSourcesConfiguration(sourceSwitches)
// Checkout remote sources.
checkoutRemoteSources(sourceSwitches)
// Replace dependencies with sources.
replaceDependenciesWithSources(sourceSwitches)
  • 首先,Gradle 在 setting 階段解析上述配置文件
  • 然后,根據(jù)解析的結(jié)果,將打開(kāi)源碼的工程通過(guò) project 的形式引用到項(xiàng)目中
  • 最后,根據(jù)上述配置文件,將項(xiàng)目中的依賴替換為工程引用

2.2 為項(xiàng)目動(dòng)態(tài)添加子工程

如上所述,這里我們忽略掉 json 配置文件解析的環(huán)節(jié),直接看拉取最新分支并將其作為子項(xiàng)目添加到項(xiàng)目中的邏輯。該部分代碼實(shí)現(xiàn)如下,

/** Checkout remote sources if necessary. */
def checkoutRemoteSources(sourceSwitches) {
    def settings = getSettings()
    def rootAbsolutePath = settings.rootDir.absolutePath
    def sourcesRootPath = new File(rootAbsolutePath).parent
    def sourcesDirectory = new File(sourcesRootPath, "open_sources")
    if (!sourcesDirectory.exists()) sourcesDirectory.mkdirs()
    sourceSwitches.forEach { name, sourceSwitch ->
        if (sourceSwitch.open) {
            def sourceDirectory = new File(sourcesDirectory, name)
            if (!sourceDirectory.exists()) {
                logd("clone start [$name] branch [${sourceSwitch.branch}]")
                "git clone -b ${sourceSwitch.branch} ${sourceSwitch.url} ".execute(null, sourcesDirectory).waitFor()
                logd("clone completed [$name] branch [${sourceSwitch.branch}]")
            } else {
                def sb = new StringBuffer()
                "git rev-parse --abbrev-ref HEAD ".execute(null, sourceDirectory).waitForProcessOutput(sb, System.err)
                def currentBranch = sb.toString().trim()
                if (currentBranch != sourceSwitch.branch) {
                    logd("checkout start current branch [${currentBranch}], checkout branch [${sourceSwitch.branch}]")
                    def out = new StringBuffer()
                    "git pull".execute(null, sourceDirectory).waitFor()
                    "git checkout -b ${sourceSwitch.branch} origin/${sourceSwitch.branch}"
                            .execute(null, sourceDirectory).waitForProcessOutput(out, System.err)
                    logd("checkout completed: ${out.toString().trim()}")
                }
            }
            // After checkout sources, include them as subprojects.
            sourceSwitch.children.each { child ->
                settings.include(":${child.name}")
                settings.project(":${child.name}").projectDir = new File(sourcesDirectory, child.path)
            }
        }
    }
}

這里,我將子項(xiàng)目的源碼克隆到 settings.gradle 文件的父目錄下的 open_sources 目錄下面。這里當(dāng)該目錄不存在的時(shí)候,我會(huì)先創(chuàng)建該目錄。這里需要注意的是,我在組織項(xiàng)目目錄的時(shí)候比較喜歡將項(xiàng)目的子工程放到和主工程一樣的位置。所以,上述克隆方式可以保證克隆到的 open_sources 仍然在當(dāng)前項(xiàng)目的工作目錄下。

然后,我對(duì) sourceSwitches,也就是解析的 json 文件數(shù)據(jù),進(jìn)行遍歷。這里會(huì)先判斷指定的源碼是否已經(jīng)拉下來(lái),如果存在的話就執(zhí)行 checkout 操作,否則執(zhí)行 clone 操作。這里在判斷當(dāng)前分支是否為目標(biāo)分支的時(shí)候使用了 git rev-parse --abbrev-ref HEAD 這個(gè) Git 指令。該指令用來(lái)獲取當(dāng)前倉(cāng)庫(kù)所處的分支。

最后,將源碼拉下來(lái)之后通過(guò) Settingsinclude() 方法加載指定的子工程,并使用 Settingsproject() 方法指定該子工程的目錄。這和我們?cè)?settings.gradle 文件中添加子工程的方式是相同的,

include ':utils-core', ':utils-ktx'
project(':utils-core').projectDir = new File('../AndroidUtils/utils')
project(':utils-ktx').projectDir = new File('../AndroidUtils/utils-ktx')

2.3 使用子工程替換依賴

動(dòng)態(tài)替換工程依賴使用的是 Gradle 的 ResolutionStrategy 這個(gè)功能。也許你對(duì)諸如

configurations.all {
  resolutionStrategy.force 'io.reactivex.rxjava2:rxjava:2.1.6'
}

這種寫法并不陌生。這里的 forcedependencySubstitution 一樣,都屬于 ResolutionStrategy 提供的功能的一部分。只不過(guò)這里的區(qū)別是,我們需要對(duì)所有的子項(xiàng)目進(jìn)行動(dòng)態(tài)更改,因此需要等項(xiàng)目 loaded 完成之后才能執(zhí)行。

下面是依賴替換的實(shí)現(xiàn)邏輯,

/** Replace dependencies with sources. */
def replaceDependenciesWithSources(sourceSwitches) {
    def gradle = settings.gradle
    gradle.projectsLoaded {
        gradle.rootProject.subprojects {
            configurations.all {
                resolutionStrategy.dependencySubstitution {
                    sourceSwitches.forEach { name, sourceSwitch ->
                        sourceSwitch.children.each { child ->
                            substitute module("${sourceSwitch.artifact}:${child.name}") with project(":${child.name}")
                        }
                    }
                }
            }
        }
    }
}

這里使用 Gradle 的 projectsLoaded 這個(gè)點(diǎn)進(jìn)行 hook,將依賴替換為子工程。

此外,也可以將子工程替換為依賴,比如,

dependencySubstitution {
  substitute module('org.gradle:api') using project(':api')
  substitute project(':util') using module('org.gradle:util:3.0')
}

2.4 注意事項(xiàng)

上述實(shí)現(xiàn)方式要求多個(gè)子工程的腳本盡可能一致。比如,在 AndroidUtils 的獨(dú)立工程中,我通過(guò) kotlin_version 這個(gè)變量指定 kotlin 的版本,但是在 android-mvvm-and-architecture 這個(gè)工程中使用的是 kotlinVersion. 所以,當(dāng)切換了子工程的源碼之后就會(huì)發(fā)現(xiàn) kotlin_version 這個(gè)變量找不到了。因此,為了實(shí)現(xiàn)可以動(dòng)態(tài)切換源碼,是需要對(duì) Gradle 腳本做一些調(diào)整的。

在我的實(shí)現(xiàn)方式中,我并沒(méi)有將子工程的源碼放到主工程的根目錄下面,也就是將 open_sources 這個(gè)目錄放到 appshell 這個(gè)目錄下面。而是放到和 appshell 同一級(jí)別。

這樣做的原因是,實(shí)際開(kāi)發(fā)過(guò)程中,通常我們會(huì)克隆很多倉(cāng)庫(kù)到 open_sources 這個(gè)目錄下面(或者之前開(kāi)發(fā)遺留下來(lái)的克隆倉(cāng)庫(kù))。有些倉(cāng)庫(kù)雖然我們關(guān)閉了源碼依賴,但是因?yàn)樵?appshell 目錄下面,依然會(huì)出現(xiàn)在 Android Studio 的工程目錄里。而按照上述方式組織目錄,我切換了哪個(gè)項(xiàng)目等源碼,哪個(gè)項(xiàng)目的目錄會(huì)被 Android Studio 加載。其他的因?yàn)椴辉?appshell 目錄下面,所以會(huì)被 Android Studio 忽略。這種組織方式可以盡可能減少 Android Studio 加載的文本,提升 Android Studio 響應(yīng)的速率。

總結(jié)

上述是開(kāi)發(fā)過(guò)程中替換依賴為源碼的“無(wú)痕”修改方式。不論在組件化還是非組件化需要開(kāi)發(fā)中都是一種非常實(shí)用的開(kāi)發(fā)技巧。按照上述開(kāi)發(fā)開(kāi)發(fā)方式,我們可以既能開(kāi)發(fā) android-mvvm-and-architecture 的時(shí)候隨時(shí)隨地打開(kāi) AndroidUtils 進(jìn)行修改,亦可對(duì) AndroidUtil 這個(gè)工程獨(dú)立編譯和開(kāi)發(fā)。

源代碼參考 android-mvvm-and-architecture 項(xiàng)目(當(dāng)前是 feature-3.0 分支)的 AppShell 下面的 sources.gradle 文件。

以上就是Gradle 依賴切換源碼實(shí)踐示例詳解的詳細(xì)內(nèi)容,更多關(guān)于Gradle 依賴切換的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Android Drawerlayout側(cè)拉欄事件傳遞問(wèn)題的解決方法

    Android Drawerlayout側(cè)拉欄事件傳遞問(wèn)題的解決方法

    這篇文章主要為大家詳細(xì)介紹了Android Drawerlayout側(cè)拉欄事件傳遞問(wèn)題的解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-11-11
  • Android中activity從創(chuàng)建到顯示的基本介紹

    Android中activity從創(chuàng)建到顯示的基本介紹

    這篇文章主要給大家介紹了關(guān)于Android中activity從創(chuàng)建到顯示的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)各位Android初學(xué)者們具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起看看吧。
    2017-11-11
  • Android RecyclerView實(shí)現(xiàn)拼團(tuán)倒計(jì)時(shí)列表實(shí)例代碼

    Android RecyclerView實(shí)現(xiàn)拼團(tuán)倒計(jì)時(shí)列表實(shí)例代碼

    這篇文章主要給大家介紹了關(guān)于Android RecyclerView實(shí)現(xiàn)拼團(tuán)倒計(jì)時(shí)列表的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)各位Android開(kāi)發(fā)者們具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-09-09
  • Android實(shí)現(xiàn)在列表List中顯示半透明小窗體效果的控件用法詳解

    Android實(shí)現(xiàn)在列表List中顯示半透明小窗體效果的控件用法詳解

    這篇文章主要介紹了Android實(shí)現(xiàn)在列表List中顯示半透明小窗體效果的控件用法,結(jié)合實(shí)例形式分析了Android半透明提示框的實(shí)現(xiàn)與設(shè)置技巧,需要的朋友可以參考下
    2016-06-06
  • Android判斷某個(gè)權(quán)限是否開(kāi)啟的方法

    Android判斷某個(gè)權(quán)限是否開(kāi)啟的方法

    今天小編就為大家分享一篇Android判斷某個(gè)權(quán)限是否開(kāi)啟的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-07-07
  • Android自定義控件實(shí)現(xiàn)時(shí)間軸

    Android自定義控件實(shí)現(xiàn)時(shí)間軸

    這篇文章主要為大家詳細(xì)介紹了Android自定義控件實(shí)現(xiàn)時(shí)間軸,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-04-04
  • Android和JavaScript相互調(diào)用的方法

    Android和JavaScript相互調(diào)用的方法

    這篇文章主要介紹了Android和JavaScript相互調(diào)用的方法,實(shí)例分析了Android的WebView執(zhí)行JavaScript及JavaScript訪問(wèn)Android的技巧,需要的朋友可以參考下
    2015-12-12
  • Android自定義SwipeLayout仿QQ側(cè)滑條目

    Android自定義SwipeLayout仿QQ側(cè)滑條目

    這篇文章主要為大家詳細(xì)介紹了Android自定義SwipeLayout仿QQ側(cè)滑條目,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-08-08
  • Android自帶的四種線程池使用總結(jié)

    Android自帶的四種線程池使用總結(jié)

    本篇文章主要介紹了Android自帶的四種線程池使用總結(jié),詳細(xì)的介紹了4種線程池的用法,具有一定的參考價(jià)值,有興趣的小伙伴可以了解一下
    2017-07-07
  • Android如何跳轉(zhuǎn)到應(yīng)用商店的APP詳情頁(yè)面

    Android如何跳轉(zhuǎn)到應(yīng)用商店的APP詳情頁(yè)面

    最近做項(xiàng)目遇到這樣的需求,要求從App內(nèi)部點(diǎn)擊按鈕或鏈接,跳轉(zhuǎn)到應(yīng)用商店的某個(gè)APP的詳情頁(yè)面,怎么實(shí)現(xiàn)此功能呢?下面小編給大家分享Android如何跳轉(zhuǎn)到應(yīng)用商店的APP詳情頁(yè)面,需要的朋友參考下
    2017-01-01

最新評(píng)論