天天看點

利用 Gradle 建構 Android 前置後置任務的詳細介紹

“建構 Android 前置後置任務” 指的是打 Android 包的時候通常會有的之前的一些操作以及之後的一些操作。例如在打包前自動修改版本号、打包成功後進行加強等等。

上一篇簡單介紹了将 Android 建構從 python / shell 等腳本語言轉成使用 gradle 的大概的思路,本篇我來詳細講述一下具體的操作流程,以及在最後說一些筆者碰到的問題。希望讀者看完以後,可以

了解使用 gradle 建構 Android 應用的代碼架構

在相關文檔的幫助下,可以自己實作相關處理步驟。(增加前置、後置任務等)

如果直接進行使用 gradle 的代碼架構的說明以及代碼展示的話,可能會使人丈二和尚摸不着頭腦。是以我從我自己容易了解的角度出發,介紹一下這種改動的比較容易了解的思路。以我的了解能力可以了解的話,相信讀者朋友們都是可以看明白這樣做的得失的。

假設 :

我們已經有一個這樣的 python 腳本用來給 Android 打包,它有如下特征:

存在了很久,從三年前項目開始就存在,後面是一系列地修修補補。

功能複雜, 功能包括但不限于:

1)打包前:根據配置檔案修改版本号;根據本次打包的某個特征值修改 gradle 編譯參數(甚至因為不同時期的修改可能使用了兩種不同的方法修改不同的參數——不過這裡簡單考慮就不需要那麼複雜啦)

2)打包後:根據打包配置進行加強;并且打出多個管道包;将包上傳到一個指定的存儲平台。

根據打包時候配置的測試環境或者是線上環境,上面提到的功能每個都不一樣(比如說測試環境上傳到一個位置,而線上環境是另外一個。)

因為沒有專人維護,每次打包需要新的功能的時候就進行改動,導緻代碼架構很差。修改起來很麻煩。

有兩個不同的應用 productA &amp; productB 都使用了這個打包腳本,使得打包腳本裡有很多對此的 <code>if(currentProduct == productA ){ }</code> 的邏輯。

然後我們接收到了一個任務 : 将這個打包腳本重構成容易了解、易于擴充的形式。

🤔,那麼我們應該怎麼做呢?以筆者的拙見,如下的思路是比較好的一種方案:

不同功能的實作代碼内聚到一起,做成一個個獨立的 python-module 用來提供單一的功能。例如可以

将加強的代碼整理成一個 <code>jiaguModule</code>,對外暴露一個 <code>jiagu(srcApk, dstApk , jiaguMethod ) #參數分别表示 加強源 / 生成 apk 的位置 / 加強方式</code>方法。

将生成管道包的代碼整理成一個 <code>channelModule</code> ,對外暴露一個 <code>buildChannels(srcApk , channels)# 參數分别表示 管道源 apk / 要生成哪些管道資訊</code> 。

兩個不同的應用使用不同的腳本,不混在一起,在腳本裡将上述子產品提供的功能組裝起來實作互相獨立的建構。

如此一來,相當于我們将之前的 python 腳本拆成了兩部分 : 一部分是封裝的功能子產品; 一部分是腳本内容。而在腳本内容裡,實際上是一些基于封裝的功能子產品的流程設計。

這裡的 封裝功能子產品 也可以使用單獨的函數來實作。

這樣我們就得到了下面的新的 Python 腳本:

封裝的功能子產品

修改版本号子產品

加強子產品

打管道包子產品

上傳子產品

實際腳本子產品

productA 的建構腳本

productB 的建構腳本

這個重構的 python 腳本很棒,隻是有一個缺點:它是 python 語言的,對于 Android 開發而言,其可讀性以及可維護性都不那麼高,如果可以改成 kotlin 的話,那就太讓人欣慰了。

然後我們來把這個重構後的 python 腳本修改成 gradle 腳本的形式。為什麼需要修改呢?因為我們希望将 python 改為熟悉的 kotlin ,以更好了解和修改。改動很簡單,隻是将 封裝的功能子產品修改成 gradle task , 實際腳本子產品修改成 gradle 腳本 。為了防止有的讀者不了解 gradle task 的基本知識,下面簡單介紹一下筆者所了解的 gradle task。

本節需要自行了解一下 buildSrc 的使用方法

gradle task 應該如何聲明? 這裡有一個簡單的方法,我們在 Android 項目根目錄下建立一個目錄叫做 <code>buildSrc</code> ,這個目錄的用法和普通的 android 子產品一樣,可以添加依賴庫如 okhttp 等。和普通 android 子產品不同之處在于這個目錄裡的源碼會在 gradle 腳本建構之前 編譯,也就是說在 gradle 腳本裡,可以直接通路到這些類以及這些類的資料(比如說常量等)。 至于 <code>buildSrc</code>具體的用法以及目錄裡面怎麼設定依賴庫等。就不在此詳細說明了。因為這畢竟不是本文的重點,如果有讀者想了解的話,建議搜尋一下網上相關的文章,有很多淺顯易懂的文章的。搜尋關鍵詞 “android buildSrc 怎麼用” 即可。 難讀的官方文檔位址:如何使用 kotlin 聲明 task 類型 。如果發現很難了解的話,也很正常,不需要強行讀,可以檢視一些其他文章或者留言問筆者…… 如果有讀者不會使用 buildSrc 的話,建議先了解一下,這樣才能知道後面具體該如何操作。不過這并不影響大體的流程,在這裡隻需要知道 buildSrc 裡的類可以在 gradle 腳本中被通路到即可。而 gradle task 就可以被放到 buildSrc 子產品中,在腳本中像通路第三方庫代碼一樣使用。

gradle task 的表現形式一直是很吓人的,要麼是 Android gradle plugin ,要麼是第三方 plugin ,這些實在是太難懂啦,筆者也搞不清楚。是以在這裡不妨先粗略簡單地了解一下 : gradle task 其實就是一個可執行函數(先忽略掉它所有的 doFirst / doLast 之類的屬性)。

上面重構後的 python 腳本裡,其中的 封裝的功能子產品 就會被轉移成 gradle task 的形式。這個的意味也很明顯:表示這些 task 是用來提供功能的,就像是函數調用一般。

不過我們寫好了 task,也隻是寫好了功能而已,并沒有調用,是無法生效的。接下來說下如何調用這些 task 來執行各種操作。

在 gradle 裡,各個 task 是可以互相建構依賴關系的,我們在上面聲明了很多功能 task,就是在 gradle 腳本中進行使用的。至于 gradle 的使用,一言難盡,筆者無力叙說很詳細,是以直接在下面列出了 demo 代碼。請各位檢視,如果有相關疑惑或者指正,歡迎提出👏🏻。

下面會列出 demo 代碼,在此先口頭說明下需要哪些改動,以使得讀者更加了解。(舉例不夠嚴謹,讀者見諒)

增加一個打包前執行的操作。這裡以修改版本号為例

增加一個打包後執行的操作。這裡以打包後加強為例

打包步驟設計腳本。

輔助幫助上面三者跑起來的 gradle 腳本的改動(包含兩個,一個是 buildSrc 的依賴配置;一個是引入上面的步驟設計腳本)

改動如下

<code>*.gradle.kts</code> 檔案沒有文法高亮。

打開 “Preferences” -&gt; “Language &amp; Frameworks” -&gt; “Kotlin” -&gt; “Kotlin Scripting” 右邊下面的 “Gradle Kotlin DSL Scripts” 中添加指定的 .gradle.kts 檔案才行。 添加之後,Android Studio 就會進行文法高亮和自動提示了。

我在 gradle.kts 檔案裡有個任務先修改了某個 build.gradle 裡的屬性,然後才執行 <code>assembleRelease</code> ,但是打出來的 apk 這個改動沒有生效,為什麼?

這是因為在調用 gradle 執行任務開始後,再去修改 gradle 檔案已經是不生效了。 比如在上面的 demo 中,如果直接執行 <code>./gradlew :app:jiagu</code> 的話, 雖然 <code>assembleRelease</code> 是中間的任務,但是其執行的時候也是以調用 <code>./gradlew :app:jiagu</code> 時候的 gradle 腳本内容為準的。 是以在 gradle.kts 裡想使用任務 A 修改某個 build.gradle 檔案,然後再打包的話。必須先執行該指令才行。即需要: <code>./gradlew :app:A</code> (該任務修改 app/build.gradle , 比如說替換了裡面聲明的 app_name 屬性) <code>./gradlew :app:jiagu</code> (舉例,表示最終任務。此處還以加強為最終目标)

為什麼我的腳本裡使用 <code>tasks.findByName("assembleRelease")</code> 找不到任務?

可能是因為沒有在 <code>project.afterEvaluate { }</code> 來擷取任務。 <code>assembleRelease</code> 任務是 android 插件執行後才會存在的,是以需要等到項目 evaluate 完成,這時候插件建立的任務才會存在。 可能是 <code>:app</code> 子產品沒有 <code>assembleRelease</code> 任務。 比如說 設定了 flavor 等情況,那時候需要視具體的任務名進行修改。

在 buildSrc 裡添加了依賴項(比如 okhttp / gson 等),但是 build/src 裡的代碼無法自動 import 三方庫的類,找不到類,這是為什麼?

這個我感覺是 AS 的 BUG,實際是存在的,隻是此時的自動 import 失效了。 我的解決方法是手動檢視某個類的位置,然後手動輸入 import 。比如說先檢視到 <code>OkHttpClient</code> 的包名是 <code>okhttp3</code>,然後在要使用的檔案頭部輸入 <code>import okhttp3.OkHttpClient</code> 。

繼續閱讀