日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

replugin源码解析之replugin-host-gradle(宿主的gradle插件)

發布時間:2025/3/15 编程问答 16 豆豆
生活随笔 收集整理的這篇文章主要介紹了 replugin源码解析之replugin-host-gradle(宿主的gradle插件) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

replugin-host-gradle 是 RePlugin 插件框架中的宿主gradle插件,主要用于在宿主應用的編譯期常規構建任務流中,插入一些定制化的構建任務,以便實現自動化編譯期修改宿主應用的目的。
RePlugin 是一套完整的、穩定的、適合全面使用的,占坑類插件化方案,由360手機衛士的RePlugin Team研發,也是業內首個提出”全面插件化“(全面特性、全面兼容、全面使用)的方案。

注:文中會提及兩種插件,請閱讀本文時注意提及插件的上下文情景,避免混淆概念:

  • replugin插件:即replugin插件化框架所指的插件,這個插件指android應用業務拆分出的獨立模塊,是android應用或模塊。
  • gradle插件:即gradle構建所需的構建插件,是gradle應用或模塊。

結構概覽

結構概覽 - 英文高清大圖 —————— 結構概覽 - 中文高清大圖

replugin-host-gradle,針對宿主應用執行的構建任務:

  • 生成帶 RePlugin 插件坑位的 AndroidManifest.xml(允許自定義數量)
  • 生成 RepluginHostConfig 類,方便插件框架讀取并自定義其屬性
  • 生成 plugins-builtin.json,json中含有插件應用的信息,包名,插件名,插件路徑等。

replugin-host-gradle 插件的構建任務基于{productFlavors}{buildTypes}組合出多維構建任務,在android gradle 插件構建規則內執行構建任務,舉個具體的例子:
在宿主中配置了 兩個渠道{baidu} {xiaomi},兩個編譯類型{debug} {release}
共會生成四種編譯組合:
{baidu}{debug} {xiaomi}{debug} {baidu}{release} {xiaomi}{release}
每種組合都會執行經由replugin-host-gradle 插件插入或修改到默認構建任務流中的gradle task為:
rpGenerate{productFlavors}{buildTypes}HostConfig - 生成RePluginHostConfig.java配置文件到buildConfig目錄下
process{productFlavors}{buildTypes}Manifest - 拼裝生成 AndroidManifest.xml(坑位組件+原xml中的組件)
rpGenerate{productFlavors}{buildTypes}BuiltinJson - 生成插件信息文件plugins-builtin.json到assets目錄下

目錄概覽

1234567891011121314151617181920212223242526272829303132\qihoo\RePlugin\replugin-host-gradle\src└─main ├─groovy └─com └─qihoo360 └─replugin └─gradle └─host AppConstant.groovy # 程序常量定義區 RePlugin.groovy # 針對宿主的特定構建任務創建及調度 ├─creator FileCreators.groovy # 組裝生成器 IFileCreator.groovy # 文件生成器接口 └─impl ├─java RePluginHostConfigCreator.groovy # RePluginHostConfig.java 生成器 └─json PluginBuiltinJsonCreator.groovy # plugins-builtin.json 生成器 PluginInfo.groovy # 插件信息模型 PluginInfoParser.groovy # 從 manifest 的 xml 中抽取 PluginInfo信息 └─handlemanifest ComponentsGenerator.groovy # 動態生成插件化框架中需要的組件 └─resources └─META-INF └─gradle-plugins replugin-host-gradle.properties # 指定 gradle 插件實現類

replugin-host-gradle的基本用法

  • 添加 RePlugin Host Gradle 依賴
    在項目根目錄的 build.gradle(注意:不是 app/build.gradle) 中添加 replugin-host-gradle 依賴:
    123456buildscript {dependencies {classpath 'com.qihoo360.replugin:replugin-host-gradle:2.1.5'...}}

在項目的app模塊中的build.gradle應用插件:

1apply plugin: 'replugin-host-gradle'

replugin-host-gradle的源碼解析

我們在開始閱讀源碼前,要思考下,replugin-host-gradle是什么?
A:replugin-host-gradle是一個自定義的gradle插件。
這個清楚了,那就上車吧。

講解replugin-host-gradle源碼的同時,還會講解一些開發自定義gradle插件的知識,希望能和您一起:知其然,亦知其所以然。

replugin-host-gradle.properties文件

1implementation-class=com.qihoo360.replugin.gradle.host.Replugin

在開發自定義gradle插件時,都會先定義這么個文件。這里有 2 個知識點:

  • 文件中的implementation-class用來指定插件實現類。
  • 文件名用來指定插件名,即在宿主中使用插件時的apply plugin: 'replugin-host-gradle'中的replugin-host-gradle.

我們到插件實現類看看這個插件是如何工作的。

此 gradle 插件基于 groovy 開發,groovy 也是 JVM 系的編程語言,對于 java 系程序員來說,幾乎可以閉著眼就開擼代碼,不過 gradle 基于 Groovy,build 腳本使用 Groovy 編寫,想寫出 gradle style 的代碼,還是可以去學學這門語言。

RePlugin.groovy文件

1234567public class Replugin implements Plugin<Project> { @Override public void apply(Project project) {println "${TAG} Welcome to replugin world ! "...}}

定義了一個類RePlugin,繼承自gradle-api 庫中的接口類 Plugin ,實現了apply接口方法,apply方法會在 build.gradle 中執行 apply plugin: ‘replugin-host-gradle’ 時被調用。

那我們分小節,循序漸進的看看 apply 方法的具體實現。

預生成AndroidManifest.xml中的組件坑位

123456789101112131415161718192021222324252627282930@Override public void apply(Project project) {println "${TAG} Welcome to replugin world ! " this.project = project /* Extensions */project.extensions.create(AppConstant.USER_CONFIG, RepluginConfig) if (project.plugins.hasPlugin(AppPlugin)) { def android = project.extensions.getByType(AppExtension)android.applicationVariants.all { variant ->addShowPluginTask(variant) if (config == null) {config = project.extensions.getByName(AppConstant.USER_CONFIG)checkUserConfig(config)} def appID = variant.generateBuildConfig.appPackageNameprintln "${TAG} appID: ${appID}" def newManifest = ComponentsGenerator.generateComponent(appID, config) ...}}}
  • 首先向Plugin傳遞參數,通過project.extensions.create(AppConstant.USER_CONFIG, RepluginConfig),將RepluginConfig類的常量配置信息賦值給AppConstant.USER_CONFIG,在接下來checkUserConfig(config)檢查配置信息時有用到,主要檢查配置信息數據類型是否正確。
  • 判斷project中是否含有AppPlugin類型插件,即是否有’application’ projects類型的Gradle plugin。我們在宿主項目中是應用了該類型插件的:apply plugin: 'com.android.application'.
    如果希望判斷是否有libraryPlugin,可以這樣寫:if (project.plugins.hasPlugin(LibraryPlugin)),it’s for ‘library’ projects.
  • 獲取project中的AppExtension類型extension,即com.android.application projects的android extension.也就是在你的app模塊的build.gradle中定義的閉包:
    123android {...}

遍歷android extension的Application variants 列表。這里說下,這可以說是 Hook Android gradle 插件的一種方式,因為通過遍歷applicationVariants,你可以修改屬性,名字,描述,輸出文件名等,如果是Android library庫,那么就將applicationVariants替換為libraryVariants。很多人可能在build.gradle中這樣定義過閉包:

1234567891011buildTypes {release {applicationVariants.all { variant ->variant.outputs.each { output -> def outputFile = output.outputFile def fileName = "xxx_${variant.productFlavors[0].name}_v${defaultConfig.versionName}_${releaseTime()}.apk"output.outputFile = new File(outputFile.parent, fileName)}}}}

其實這也是一種插件的創建方式,Hook Android gradle 插件動態修改variants屬性值,修改打包輸出的apk文件名。
創建自定義gradle插件,Gradle提供了多種方式:

  • 在build.gradle腳本中直接創建(上述代碼即是)
  • 在獨立Module中創建(replugin-host-gradle即是)
    • 繼續看代碼,addShowPluginTask(variant)這個方法執行了,但是方法內指定的task并未掛到android gradle task上,即task不會執行。這個task是方便調試時查看插件信息的,任務內容同接下來將講到的生成 plugins-builtin.json 插件信息文件task一致。
    • checkUserConfig(config),獲取到AppConstant.USER_CONFIG內一系列參數后,做數據類型正確性校驗。
    • 關鍵代碼來了,下面一行代碼,搞定了宿主中AndroidManifest.xml中的組件坑位生成,注意,結合結構概覽中的gradle Flow 看,這里只是生成組件坑位的xml代碼,最終的xml文件是在后續的task中拼裝出來的,稍后會講到。
      1def newManifest = ComponentsGenerator.generateComponent(appID, config)

在代碼面前,一切都是紙老虎。上車,進去看如何生成坑位的。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 def static generateComponent(def applicationID, def config) { // 是否使用 AppCompat 庫(涉及到默認主題) if (config.useAppCompat) { themeNTS = THEME_NTS_NOT_APP_COMPAT } else { themeNTS = THEME_NTS_NOT_USE_APP_COMPAT } def writer = new StringWriter() def xml = new MarkupBuilder(writer) /* UI 進程 */ xml.application { /* 透明坑 */ config.countTranslucentStandard.times { activity( "${name}": "${applicationID}.${infix}N1NRTS${it}", "${cfg}": "${cfgV}", "${exp}": "${expV}", "${ori}": "${oriV}", "${theme}": "${themeTS}") ... } ... /* 不透明坑 */ config.countNotTranslucentStandard.times{ } ... } // 刪除 application 標簽 def normalStr = writer.toString().replace("<application>", "").replace("</application>", "") // println "${TAG} normalStr: ${normalStr}" // 將單進程和多進程的組件相加 normalStr + generateMultiProcessComponent(applicationID, config) }

一定要用一句話總結的話,那就是:基于 Groovy 的 MarkupBuilder api,根據 RepluginConfig 類中的配置,拼出組件坑位的xml 字符串。
就像搭積木一樣,看一組就明白了。
生成坑位的代碼:

12345678config.countTranslucentStandard.times {activity( "${name}": "${applicationID}.${infix}N1NRTS${it}", "${cfg}": "${cfgV}", "${exp}": "${expV}", "${ori}": "${oriV}", "${theme}": "${themeTS}")}

注:config.countTranslucentStandard.times 含義:根據config.countTranslucentStandard的值循環
生成的坑位:

123456<activity android:theme="@ref/0x01030010" android:name="com.qihoo360.replugin.sample.host.loader.a.ActivityN1NRTS0" android:exported="false" android:screenOrientation="1" android:configChanges="0x4b0" />

一個字總結:replace.

Tips. 可以用Android Studio的Analyze APK…功能查看host gradle插件構建后宿主的AndroidManifest.xml,看看生成的坑位的樣子就明白了。

##生成 RePluginHostConfig 配置文件

12345678910111213141516171819202122232425262728293031323334353637@Override public void apply(Project project) {... if (project.plugins.hasPlugin(AppPlugin)) { def android = project.extensions.getByType(AppExtension)android.applicationVariants.all { variant ->... def variantData = variant.variantData def scope = variantData.scope //host generate task def generateHostConfigTaskName = scope.getTaskName(AppConstant.TASK_GENERATE, "HostConfig") def generateHostConfigTask = project.task(generateHostConfigTaskName)generateHostConfigTask.doLast {FileCreators.createHostConfig(project, variant, config)}generateHostConfigTask.group = AppConstant.TASKS_GROUP //depends on build config taskString generateBuildConfigTaskName = variant.getVariantData().getScope().getGenerateBuildConfigTask().name def generateBuildConfigTask = project.tasks.getByName(generateBuildConfigTaskName) if (generateBuildConfigTask) {generateHostConfigTask.dependsOn generateBuildConfigTaskgenerateBuildConfigTask.finalizedBy generateHostConfigTask}...}}}

繼續回到 apply 方法,接下來該到生成 RePluginHostConfig 的時候了,即 注釋中的host generate task。

  • 首先生成了 HostConfig 的gradle task 名字,并調用project的task()方法創建此Task。
  • 指定了 generateHostConfigTask 的task任務:自動創建RePluginHostConfig.java至BuildConfig目錄。
    123generateHostConfigTask.doLast {FileCreators.createHostConfig(project, variant, config)}

注:createHostConfig(...)方法內的實現,也是根據配置類 RepluginConfig中的配置信息拼裝生成的java文件。

  • 設置generateHostConfigTask的執行依賴
    12345//depends on build config taskif (generateBuildConfigTask) {generateHostConfigTask.dependsOn generateBuildConfigTaskgenerateBuildConfigTask.finalizedBy generateHostConfigTask}

因為此task中創建的RePluginHostConfig.java希望放置到編譯輸出目錄..\replugin-sample\host\app\build\generated\source\buildConfig\{productFlavors}\{buildTypes}\...下,所以此task依賴于生成 BuildConfig.java 的task并設置為 BuildConfigTask 執行完后,就執行HostConfigTask。
關于gradle 的 task 相關知識,可以去gradle 官網或某搜索引擎查看學習,屬于字典型知識點,需要時候查閱下。

##生成 plugins-builtin.json 插件信息文件

12345678910111213141516171819202122232425262728293031323334@Override public void apply(Project project) {... if (project.plugins.hasPlugin(AppPlugin)) { def android = project.extensions.getByType(AppExtension)android.applicationVariants.all { variant ->... //json generate task def generateBuiltinJsonTaskName = scope.getTaskName(AppConstant.TASK_GENERATE, "BuiltinJson") def generateBuiltinJsonTask = project.task(generateBuiltinJsonTaskName)generateBuiltinJsonTask.doLast {FileCreators.createBuiltinJson(project, variant, config)}generateBuiltinJsonTask.group = AppConstant.TASKS_GROUP //depends on mergeAssets TaskString mergeAssetsTaskName = variant.getVariantData().getScope().getMergeAssetsTask().name def mergeAssetsTask = project.tasks.getByName(mergeAssetsTaskName) if (mergeAssetsTask) {generateBuiltinJsonTask.dependsOn mergeAssetsTaskmergeAssetsTask.finalizedBy generateBuiltinJsonTask}...}}}

繼續回到 apply 方法,接下來該到生成 plugins-builtin.json 這個包含了插件信息的文件的時候了,即 注釋中的json generate task。

  • 首先生成個gradle task 名字,并調用project的task()方法創建此Task。
  • 指定了 generateBuiltinJsonTask 的task任務:掃描宿主\assets\plugins目錄下的插件文件,并基于apk文件規則解析出插件信息,包名,版本號等,然后拼裝成json文件。

    123generateBuiltinJsonTask.doLast {FileCreators.createBuiltinJson(project, variant, config)}
  • 設置 generateBuiltinJsonTask 的執行依賴

    12345//depends on build config taskif (mergeAssetsTask) {generateBuiltinJsonTask.dependsOn mergeAssetsTaskmergeAssetsTask.finalizedBy generateBuiltinJsonTask}

因為此task中創建的 plugins-builtin.json 希望放置到編譯輸出目錄...\replugin-sample\host\app\build\intermediates\assets\{productFlavors}\{buildTypes}\...下,所以此task依賴于merge assets文件 的task并設置為 mergeAssetsTask 執行完后,就執行BuiltinJsonTask。

##拼裝 AndroidManifest.xml

12345output.processManifest.doLast { def manifestPath = output.processManifest.outputFile.absolutePath def updatedContent = new File(manifestPath).getText("UTF-8").replaceAll("</application>", newManifest + "</application>") new File(manifestPath).write(updatedContent, 'UTF-8')}
  • 將坑位 xml 字符串 與 原有xml 標簽內的配置信息合二為一。

至此,replugin-host-gradle 插件的工作就全部結束了。

##End

replugin-host-gradle 插件是一個compile-time gradle plugin,基于賦予android gradle 構建任務流中新的構建任務及修改已有的構建任務,進而實現動態修改構建目標文件的為replugin宿主服務的gradle插件。


https://wangfuda.github.io/2017/07/15/replugin-host-gradle/

總結

以上是生活随笔為你收集整理的replugin源码解析之replugin-host-gradle(宿主的gradle插件)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。