筆者有位朋友,每次新學一門語言,都會用來寫一個貪吃蛇游戲,以此來檢驗自己學習的成果。筆者也有類似體會。所謂紙上得來終覺淺,絕知此事要躬行。這一章,筆者將以開發和發布一個 Gradle 插件作為目標,加深學習成果。

官方文檔給出了比較詳細的實現步驟,本文的脈絡會跟官方文檔差不了太多,額外增補實際例子和一些實踐經驗。文中的代碼已經托管到了 github 項目中。
需求
默認的 Android 打包插件會把 apk 命名成module-productFlavor-buildType.apk,例如app-official-debug.apk,并且會把包文件發布到固定的位置:module/build/outputs/apk有的時候,這個命名風格并不是你所要的,你也想講 apk 輸出到別的目錄。咱們通過 gradle 插件來實現自定義。這個插件的需求是:
輸入一個名為 nameMap 的 Closure,用來修改 apk 名字 輸入一個名為 destDir 的 String,用于輸出位置 原理簡述 插件之于 Gradle
根據官方文檔定義,插件打包了可重用的構建邏輯,可以適用于不同的項目和構建過程。
Gradle 提供了很多官方插件,用于支持 Java、Groovy 等工程的構建和打包。同時也提供了自定義插件的機制,讓每個人都可以通過插件來實現特定的構建邏輯,并可以把這些邏輯打包起來,分享給其他人。
插件的源碼可以使用 Groovy、Scala、Java 三種語言,筆者不會 Scala,所以平時只是使用 Groovy 和 Java。前者用于實現與 Gradle 構建生命周期(如 task 的依賴)有關的邏輯,后者用于核心邏輯,表現為 Groovy 調用 Java 的代碼。
另外,還有很多項目使用 Eclipse 或者 Maven 進行開發構建,用 Java 實現核心業務代碼,將有利于實現快速遷移。
插件打包方式
Gradle 的插件有三種打包方式,主要是按照復雜程度和可見性來劃分:
Build script
把插件寫在 build.gradle 文件中,一般用于簡單的邏輯,只在該 build.gradle 文件中可見,筆者常用來做原型調試,本文將簡要介紹此類。
buildSrc 項目
將插件源代碼放在rootProjectDir/buildSrc/src/main/groovy中,只對該項目中可見,適用于邏輯較為復雜,但又不需要外部可見的插件,本文不介紹,有興趣可以參考此處。
獨立項目
一個獨立的 Groovy 和 Java 項目,可以把這個項目打包成 Jar 文件包,一個 Jar 文件包還可以包含多個插件入口,將文件包發布到托管平臺上,供其他人使用。本文將著重介紹此類。
Build script 插件
首先來直接在 build.gradle 中寫一個 plugin:
class ApkDistPlugin implements Plugin { @Override void apply(Project project) { project.task(\\\’apkdist\\\’) << { println \\\’hello, world!\\\’ } } } apply plugin: ApkDistPlugin
命令行運行
$ ./gradlew -p app/ apkdist :app:apkdist hello, world!
這個插件創建了一個名為apkdist的 task,并在 task 中打印。
插件是一個類,繼承自org.gradle.api.Plugin接口,重載void apply(Project project)方法,這個方法將會傳入使用這個插件的 project 的實例,這是一個重要的 context。
接受外部參數
通常情況下,插件使用方需要傳入一些配置參數,如 bugtags 的 SDK 的插件需要接受兩個參數:
bugtags { appKey "APP_KEY" //這里是你的 appKey appSecret "APP_SECRET" //這里是你的 appSecret,管理員在設置頁可以查看 }
同樣,ApkDistPlugin 這個 plugin 也希望接受兩個參數:
apkdistconf { nameMap { name -> println \\\’hello,\\\’ name return name } destDir \\\’your-distribution-dir\\\’ }
參數的內容后面繼續完善。那這兩個參數怎么傳到插件內呢?
org.gradle.api.Project有一個ExtensionContainer getExtensions()方法,可以用來實現這個傳遞。
聲明參數類
聲明一個 Groovy 類,有兩個默認值為 null 的成員變量:
class ApkDistExtension { Closure nameMap = null; String destDir = null; } 接受參數
project.extensions.create(\\\’apkdistconf\\\’, ApkDistExtension);
要注意,create方法的第一個參數就是你在 build.gradle 文件中的進行參數配置的 dsl 的名字,必須一致;第二個參數,就是參數類的名字。
獲取和使用參數
在 create 了 extension 之后,如果傳入了參數,則會攜帶在 project 實例中,
def closure = project[\\\’apkdistconf\\\’].nameMap; closure(\\\’wow!\\\’); println project[\\\’apkdistconf\\\’].destDir 進化版本一:參數
class ApkDistExtension { Closure nameMap = null; String destDir = null; } class ApkDistPlugin implements Plugin { @Override void apply(Project project) { project.extensions.create(\\\’apkdistconf\\\’, ApkDistExtension); project.task(\\\’apkdist\\\’) << { println \\\’hello, world!\\\’ def closure = project[\\\’apkdistconf\\\’].nameMap; closure(\\\’wow!\\\’); println project[\\\’apkdistconf\\\’].destDir } } } apply plugin: ApkDistPlugin apkdistconf { nameMap { name -> println \\\’hello, \\\’ name return name } destDir \\\’your-distribution-directory\\\’ }
運行結果:
$ ./gradlew -p app/ apkdist :app:apkdist hello, world! hello, wow! your-distribution-directory 獨立項目插件
代碼寫到現在,已經不適合再放在一個 build.gradle 文件里面了,那也不是我們的目的。建立一個獨立項目,把代碼搬到對應的地方。
理論上,IntelliJ IDEA 開發插件要比 Android Studio 要方便一點點,因為有對應 Groovy module 的模板。但其實如果我們了解 IDEA 的項目文件結構,就不會受到這個局限,無非就是一個 build.gradle 構建文件加 src 源碼文件夾。
最終項目的文件夾結構是這樣:
Java-Library
下面我們來一步步講解。
創建項目
在 Android Studio 中新建Java Librarymodule“plugin”。
修改 build.gradle 文件
添加 Groovy 插件和對應的兩個依賴。
//removed java plugin apply plugin: \\\’groovy\\\’ dependencies { compile gradleApi()//gradle sdk compile localGroovy()//groovy sdk compile fileTree(dir: \\\’libs\\\’, include: [\\\’*.jar\\\’]) } 修改項目文件夾
src/main 項目文件下:
移除 java 文件夾,因為在這個項目中用不到 java 代碼 添加 groovy 文件夾,主要的代碼文件放在這里 添加 resources 文件夾,存放用于標識 gradle 插件的 meta-data 建立對應文件
. ├── build.gradle ├── libs ├── plugin.iml └── src └── main ├── groovy │ └── com │ └── asgradle │ └── plugin │ ├── ApkDistExtension.groovy │ └── ApkDistPlugin.groovy └── resources └── META-INF └── gradle-plugins └── com.asgradle.apkdist.properties
注意:
groovy 文件夾中的類,一定要修改成.groovy后綴,IDE 才會正常識別。 resources/META-INF/gradle-plugins 這個文件夾結構是強制要求的,否則不能識別成插件。 com.asgradle.apkdist.properties 文件
如果寫過 Java 的同學會知道,這是一個 Java 的 properties 文件,是key=value的格式。這個文件內容如下:
implementation-class=com.asgradle.plugin.ApkDistPlugin
按其語義推斷,是指定這個插件的入口類。
英文敏感的同學可能會問了,為什么這個文件的承載文件夾是叫做gradle-plugins,使用復數?沒錯,這里可以指定多個 properties 文件,定義多個插件,擴展性一流,可以參考 linkedin 的插件的組織方式。
使用這個插件的時候,將會是這樣:
apply plugin:\\\’com.asgradle.apkdist\\\’
因此,com.asgradle.apkdist這個字符串在這里,又稱為這個插件的 id,不允許跟別的插件重復,取你擁有的域名的反向就不會錯。
將 plugin module 傳到本地 maven 倉庫
參考上一篇:擁抱 Android Studio 之四:Maven 倉庫使用與私有倉庫搭建,和對應的 demo 項目,將包傳到本地倉庫中進行測試。
添加 gradle.properties
PROJ_NAME=gradleplugin PROJ_ARTIFACTID=gradleplugin PROJ_POM_NAME=Local Repository LOCAL_REPO_URL=file:///Users/changbinhe/Documents/Android/repo/ PROJ_GROUP=com.as-gradle.demo PROJ_VERSION=1.0.0 PROJ_VERSION_CODE=1 PROJ_WEBSITEURL=http://kvh.io PROJ_ISSUETRACKERURL=https://github.com/kevinho/Embrace-Android-Studio-Demo/issues PROJ_VCSURL=https://github.com/kevinho/Embrace-Android-Studio-Demo.git PROJ_DESCRIPTION=demo apps for embracing android studio PROJ_LICENCE_NAME=The Apache Software License, Version 2.0 PROJ_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt PROJ_LICENCE_DEST=repo DEVELOPER_ID=your-dev-id DEVELOPER_NAME=your-dev-name DEVELOPER_EMAIL=your-email@your-mailbox.com 在 build.gradle 添加上傳功能
apply plugin: \\\’maven\\\’ uploadArchives { repositories.mavenDeployer { repository(url: LOCAL_REPO_URL) pom.groupId = PROJ_GROUP pom.artifactId = PROJ_ARTIFACTID pom.version = PROJ_VERSION } }
上傳可以通過運行:
$ ./gradlew -p plugin/ clean build uploadArchives 在 app module 中使用插件 在項目的 buildscript 添加插件作為 classpath
buildscript { repositories { maven{ url \\\’file:///Users/your-user-name/Documents/Android/repo/\\\’ } jcenter() } dependencies { classpath \\\’com.android.tools.build:gradle:2.1.0-alpha3\\\’ classpath \\\’com.as-gradle.demo:gradleplugin:1.0.0\\\’ } } 在 app module 中使用插件:
apply plugin: \\\’com.asgradle.apkdist\\\’
命令行運行:
$ ./gradlew -p app apkdist :app:apkdist hello, world! hello, wow! your-distribution-directory 可能會遇到問題
Error:(46, 0) Cause: com/asgradle/plugin/ApkDistPlugin : Unsupported major.minor version 52.0 Open File
應該是本機的 JDK 版本是1.8,默認將 plugin module 的 groovy 源碼編譯成了1.8版本的 class 文件,放在 Android 項目中,無法兼容。需要對 plugin module 的 build.gradle 文件添加兩個參數:
sourceCompatibility = 1.6 targetCompatibility = 1.6 真正的實現插件需求
讀者可能會觀察到,到目前為止,插件只是跑通了流程,并沒有實現本文提出的兩個需求,
那接下來就具體實現一下。
class ApkDistPlugin implements Plugin { @Override void apply(Project project) { project.extensions.create(\\\’apkdistconf\\\’, ApkDistExtension); project.afterEvaluate { //只可以在 android application 或者 android lib 項目中使用 if (!project.android) { throw new IllegalStateException(\\\’Must apply \\\’com.android.application\\\’ or \\\’com.android.library\\\’ first!\\\’) } //配置不能為空 if (project.apkdistconf.nameMap == null || project.apkdistconf.destDir == null) { project.logger.info(\\\’Apkdist conf should be set!\\\’) return } Closure nameMap = project[\\\’apkdistconf\\\’].nameMap String destDir = project[\\\’apkdistconf\\\’].destDir //枚舉每一個 build variant project.android.applicationVariants.all { variant -> variant.outputs.each { output -> File file = output.outputFile output.outputFile = new File(destDir, nameMap(file.getName())) } } } } }
必須指出,本文插件實現的需求,其實可以直接在 app module 的 build.gradle 中寫腳本就可以實現。這里做成插件,只是為了做示范。
上傳到 bintray 的過程,就不再贅述了,可以參考擁抱 Android Studio 之四:Maven 倉庫使用與私有倉庫搭建。
后記
至此,這系列開篇的時候挖下的坑,終于填完了。很多人借助這系列的講解,真正理解了 Android Studio 和它背后的 Gradle、Groovy,筆者十分高興。筆者也得到了很多讀者的鼓勵和支持,心中十分感激。
寫博客真的是一個很講究執行力和耐力的事情,但既然挖下了坑,就得填上,對吧?
這半年來,個人在 Android 和 Java 平臺上也做了更多的事情,也有了更多的體會。
AS 系列,打算擴充幾個主題:
Proguard 混淆 Java & Android Testing Maven 私有倉庫深入 持續集成 ……待發掘
記得有人說,只懂 Android 不懂 Java,是很可怕的。在這半年以來,筆者在工作中使用 Java 實現了一些后端服務,也認真學習了 JVM 字節碼相關的知識并把它使用到了工作中。在這個過程中,真的很為 Java 平臺的活力、豐富的庫資源、幾乎無止境的可能性所折服。接下來,會寫一些跟有關的學習體會,例如:
Java 多線程與鎖 JVM 部分原理 字節碼操作 Java 8部分特性 ……待學習
隨著筆者工作的進展,我也有機會學習使用了別的語言,例如 Node.js,并實現了一些后端服務。這個語言的活力很強,一些比 Java 現代的地方,很吸引人。有精力會寫一寫。
因為業務所需,筆者所經歷的系統,正在處于像面向服務的演化過程中,我們期望建立統一的通訊平臺和規范,抽象系統的資源,拆分業務,容器化。這是一個很有趣的過程,也是對我們的挑戰。筆者也希望有機會與讀者分享。
一不小心又挖下了好多明坑和無數暗坑,只是為了激勵自己不斷往前。在探索事物本質的旅途中,必然十分艱險,又十分有趣,沿途一定風光絢麗,讓我們共勉。
參考文獻
官方文檔
系列導讀
本文是筆者《擁抱 Android Studio》系列第四篇,其他篇請點擊:
擁抱 Android Studio 之一:從 ADT 到 Android Studio
擁抱 Android Studio 之二:Android Studio 與 Gradle 深入
擁抱 Android Studio 之三:溯源,Groovy 與 Gradle 基礎
擁抱 Android Studio 之四:Maven 公共倉庫使用與私有倉庫搭建
擁抱 Android Studio 之五:Gradle 插件使用與開發
有問題?在文章下留言或者加 qq 群:453503476,希望能幫到你。
番外
筆者 kvh 在開發和運營 bugtags.com,這是一款移動時代首選的 bug 管理系統,能夠極大的提升 app 開發者的測試效率,歡迎使用、轉發推薦。
筆者目前關注點在于移動 SDK 研發,后端服務設計和實現。
 

更多關于云服務器域名注冊,虛擬主機的問題,請訪問三五互聯官網:www.shinetop.cn

贊(0)
聲明:本網站發布的內容(圖片、視頻和文字)以原創、轉載和分享網絡內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。郵箱:3140448839@qq.com。本站原創內容未經允許不得轉載,或轉載時需注明出處:三五互聯知識庫 » 擁抱 Android Studio 之五:Gradle 插件開發

登錄

找回密碼

注冊

主站蜘蛛池模板: 亚洲精品麻豆一二三区| 亚洲欧洲国产综合aⅴ无码| 成人午夜福利免费专区无码| 别揉我奶头~嗯~啊~的视频| 人人妻人人澡人人爽| 蜜臀av一区二区精品字幕| 中文 在线 日韩 亚洲 欧美| 少妇仑乱a毛片无码| 久久久久99精品成人片| 陆河县| 国产麻豆成人传媒免费观看| 欧美精品亚洲精品日韩专| 精品成在人线av无码免费看| 一级做a爰片在线播放| 在线观看热码亚洲av每日更新| 中文字幕久久精品波多野结| 国产女人看国产在线女人| 国产精品中文字幕免费| 欧美国产日韩久久mv| 国产成熟女人性满足视频| 久久久久无码中| 久久精品国产亚洲精品2020| 无码国内精品人妻少妇| 日韩国产亚洲欧美成人图片| 国产无遮挡免费真人视频在线观看| 国产小视频一区二区三区| 亚洲日韩欧美一区二区三区在线| 推油少妇久久99久久99久久| 99国产精品白浆无码流出| 亚洲亚洲人成综合网络| 粉嫩国产一区二区三区在线| 国产精品久久久久乳精品爆| 亚洲粉嫩av一区二区黑人| 99re视频在线| 黑人异族巨大巨大巨粗| 麻豆人人妻人人妻人人片av| 国产美女裸身网站免费观看视频| 国产精品无码素人福利不卡| 国产亚洲人成网站在线观看 | 97欧美精品系列一区二区| 在线中文字幕国产精品|