Replugin 소스 해석의 replugin-host-gradle
Replugin에 대한 소개 및 간단한 사용은 공식 문서를 참조할 수 있으며, 여기서 더 이상 설명하지 않습니다.본고는 이전에 사용자 정의gradle 플러그인에 대해 어느 정도의 기초를 가지고 이해하기 쉽도록Gradle 학습-----Gradle 사용자 정의 플러그인을 참조해야 한다.NOTE: 글에서 두 가지 플러그인을 언급할 것입니다. 본문을 읽을 때 플러그인의 상하문 상황을 언급하여 개념을 혼동하지 않도록 주의하십시오.
1:replugin : replugin , android , android 。
2:gradle ( ): gradle , gradle 。
카탈로그 개요
\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
포털 클래스 링크
분해하다
1: AndroidManifest 사전 생성.xml의 구성 요소 구덩이 위치
@Override
public void apply(Project project) {
println "${TAG} Welcome to replugin world ! "
this.project = project
/* Extensions */
project.extensions.create(AppConstant.USER_CONFIG, RepluginConfig) // gradle repluginHostConfig
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.appPackageName
println "${TAG} appID: ${appID}"
def newManifest = ComponentsGenerator.generateComponent(appID, config)
...
}
}
}
checkUserConfig(config)
에 사용됩니다.만약 우리가 설정하지 않는다면 기본 설정은 다음과 같다class RepluginConfig {
/** ( UI Persistent ) */
def countProcess = 3
/** ? */
def persistentEnable = true
/** ( Persistent , )*/
def persistentName = ':GuardService'
/** */
def countNotTranslucentStandard = 6
def countNotTranslucentSingleTop = 2
def countNotTranslucentSingleTask = 3
def countNotTranslucentSingleInstance = 2
/** */
def countTranslucentStandard = 2
def countTranslucentSingleTop = 2
def countTranslucentSingleTask = 2
def countTranslucentSingleInstance = 3
/** TaskAffinity */
def countTask = 2
/**
* AppCompat
* com.android.support:appcompat-v7:25.2.0
*/
def useAppCompat = false
/** HOST */
def compatibleVersion = 10
/** HOST */
def currentVersion = 12
/** plugins-builtin.json , "plugins-builtin.json" */
def builtInJsonFileName = "plugins-builtin.json"
/** plugins-builtin.json , */
def autoManageBuiltInJsonFile = true
/** assert , assert "plugins" */
def pluginDir = "plugins"
/** , ".jar" jar */
def pluginFilePostfix = ".jar"
/** jar ( jar) , true */
def enablePluginFileIllegalStopBuild = true
}
apply plugin: 'replugin-host-gradle'
를 사용할 때는 반드시 apply plugin: 'com.android.application'
이후에 사용해야 한다.libraryPlugin이 있는지 여부를 판단하려면 다음과 같이 쓰십시오: if (project.plugins.hasPlugin(LibraryPlugin))
android { ...}
android extension의 Application variants 목록을 옮겨다니다.이것은 Hook Android gradle 플러그인의 한 방식이라고 할 수 있다. 응용 프로그램Variants를 두루 훑어보면 속성, 이름, 설명, 출력 파일 이름 등을 수정할 수 있기 때문이다. Android library 라이브러리라면 응용 프로그램Variants를libraryVariants로 대체할 수 있다.앱에 있는build입니다.gradle에서 클립을 이렇게 정의했습니다: buildTypes {
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은 다양한 방식을 제공합니다 (Gradle 학습 -----Gradle 사용자 정의 플러그인 참조): 1:build.gradle 스크립트에서 직접 만들기 (상기 코드는 다음과 같습니다) 2: 독립된 모듈에서 만들기 (replugin-host-gradle 즉)
addShowPluginTask(variant)
방법은 다음과 같다// 【 】
def addShowPluginTask(def variant) {
def variantData = variant.variantData
def scope = variantData.scope
def showPluginsTaskName = scope.getTaskName(AppConstant.TASK_SHOW_PLUGIN, "")
def showPluginsTask = project.task(showPluginsTaskName)
showPluginsTask.doLast {
IFileCreator creator = new PluginBuiltinJsonCreator(project, variant, config)
def dir = creator.getFileDir()
if (!dir.exists()) {
println "${AppConstant.TAG} The ${dir.absolutePath} does not exist "
println "${AppConstant.TAG} pluginsInfo=null"
return
}
String fileContent = creator.getFileContent()
if (null == fileContent) {
return
}
new File(dir, creator.getFileName()).write(fileContent, 'UTF-8')
}
showPluginsTask.group = AppConstant.TASKS_GROUP
//get mergeAssetsTask name
String mergeAssetsTaskName = variant.getVariantData().getScope().getMergeAssetsTask().name
//get real gradle task
def mergeAssetsTask = project.tasks.getByName(mergeAssetsTaskName)
//depend on mergeAssetsTask so that assets have been merged
if (mergeAssetsTask) {
showPluginsTask.dependsOn mergeAssetsTask
}
}
그러나 방법에서 지정한task는androidgradle task에 연결되지 않았습니다. 즉,task가 실행하지 않습니다.이task는 디버깅할 때 플러그인 정보를 보기 편합니다.
checkUserConfig
, 원본 코드는 다음과 같다: /**
*
*/
def checkUserConfig(config) {
/*
def persistentName = config.persistentName
if (persistentName == null || persistentName.trim().equals("")) {
project.logger.log(LogLevel.ERROR, "
---------------------------------------------------------------------------------")
project.logger.log(LogLevel.ERROR, " ERROR: persistentName can'te be empty, please set persistentName in replugin. ")
project.logger.log(LogLevel.ERROR, "---------------------------------------------------------------------------------
")
System.exit(0)
return
}
*/
doCheckConfig("countProcess", config.countProcess)
doCheckConfig("countTranslucentStandard", config.countTranslucentStandard)
doCheckConfig("countTranslucentSingleTop", config.countTranslucentSingleTop)
doCheckConfig("countTranslucentSingleTask", config.countTranslucentSingleTask)
doCheckConfig("countTranslucentSingleInstance", config.countTranslucentSingleInstance)
doCheckConfig("countNotTranslucentStandard", config.countNotTranslucentStandard)
doCheckConfig("countNotTranslucentSingleTop", config.countNotTranslucentSingleTop)
doCheckConfig("countNotTranslucentSingleTask", config.countNotTranslucentSingleTask)
doCheckConfig("countNotTranslucentSingleInstance", config.countNotTranslucentSingleInstance)
doCheckConfig("countTask", config.countTask)
println '--------------------------------------------------------------------------'
// println "${TAG} appID=${appID}"
println "${TAG} useAppCompat=${config.useAppCompat}"
// println "${TAG} persistentName=${config.persistentName}"
println "${TAG} countProcess=${config.countProcess}"
println "${TAG} countTranslucentStandard=${config.countTranslucentStandard}"
println "${TAG} countTranslucentSingleTop=${config.countTranslucentSingleTop}"
println "${TAG} countTranslucentSingleTask=${config.countTranslucentSingleTask}"
println "${TAG} countTranslucentSingleInstance=${config.countTranslucentSingleInstance}"
println "${TAG} countNotTranslucentStandard=${config.countNotTranslucentStandard}"
println "${TAG} countNotTranslucentSingleTop=${config.countNotTranslucentSingleTop}"
println "${TAG} countNotTranslucentSingleTask=${config.countNotTranslucentSingleTask}"
println "${TAG} countNotTranslucentSingleInstance=${config.countNotTranslucentSingleInstance}"
println "${TAG} countTask=${config.countTask}"
println '--------------------------------------------------------------------------'
}
처음에 불러온
repluginHostConfig
설정 정보를 읽고 데이터 형식의 정확성을 검사합니다.def newManifest = ComponentsGenerator.generateComponent(appID, config)
이 메서드의 소스는 다음과 같습니다.
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("", "").replace(" ", "")
// println "${TAG} normalStr: ${normalStr}"
//
normalStr + generateMultiProcessComponent(applicationID, config)
}
Groovy의 Markup Builder api를 기반으로 Replugin Config 클래스의 설정에 따라 구성 요소 슬롯의 xml 문자열을 조합하는 것이 주요 역할입니다.나무를 쌓는 것처럼 그 중 한 조를 연구로 삼았다.
config.countTranslucentStandard.times {
activity(
"${name}": "${applicationID}.${infix}N1NRTS${it}",
"${cfg}": "${cfgV}",
"${exp}": "${expV}",
"${ori}": "${oriV}",
"${theme}": "${themeTS}")
}
NOTE:
config.countTranslucentStandard.times
의 의미: config.countTranslucentStandard
의 값에 따라 순환하여 생성된 갱위:
:바꾸기
RePluginHostConfig 구성 파일 생성
@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 task
String generateBuildConfigTaskName = variant.getVariantData().getScope().getGenerateBuildConfigTask().name
def generateBuildConfigTask = project.tasks.getByName(generateBuildConfigTaskName)
if (generateBuildConfigTask) {
generateHostConfigTask.dependsOn generateBuildConfigTask
generateBuildConfigTask.finalizedBy generateHostConfigTask
}
...
}
}
}
계속해서 apply 방법으로 돌아가서 RePluginHostConfig를 생성할 때가 되었습니다. 즉, 주석에 있는
host generate task
generateHostConfigTask.doLast {
FileCreators.createHostConfig(project, variant, config)
}
주:
createHostConfig(...)
도 설정 클래스RepluginConfig
의 설정 정보를 조합하여 만든 자바 파일입니다.//depends on build config task
if (generateBuildConfigTask) {
generateHostConfigTask.dependsOn generateBuildConfigTask
generateBuildConfigTask.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 플러그인 정보 파일
@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 Task
String mergeAssetsTaskName = variant.getVariantData().getScope().getMergeAssetsTask().name
def mergeAssetsTask = project.tasks.getByName(mergeAssetsTaskName)
if (mergeAssetsTask) {
generateBuiltinJsonTask.dependsOn mergeAssetsTask
mergeAssetsTask.finalizedBy generateBuiltinJsonTask
}
...
}
}
}
계속해서 apply 방법으로 돌아가서plugins-builtin을 생성해야 합니다.json 이것은 플러그인 정보를 포함하는 파일입니다. 즉, 주석에 있는
json generate task
입니다.generateBuiltinJsonTask.doLast {
FileCreators.createBuiltinJson(project, variant, config)
}
generateBuiltinJsonTask의 실행 의존도 설정
//depends on build config task
if (mergeAssetsTask) {
generateBuiltinJsonTask.dependsOn mergeAssetsTask
mergeAssetsTask.finalizedBy generateBuiltinJsonTask
}
이task에서 만든plugins-builtin 때문에.json은 컴파일 출력 디렉터리
...\replugin-sample\host\app\build\intermediates\assets\{productFlavors}\{buildTypes}\...
에 저장하기를 원하기 때문에 이task는mergeassets 파일의task에 의존하고 mergeAssetsTask
로 설정한 후 실행BuiltinJsonTask
합니다.안드로이드 매니페스트를 조립하다.xml
output.processManifest.doLast {
def manifestPath = output.processManifest.outputFile.absolutePath
def updatedContent = new File(manifestPath).getText("UTF-8").replaceAll("", newManifest + "")
new File(manifestPath).write(updatedContent, 'UTF-8')
}
replugin-host-gradle
플러그인의 작업은 모두 끝났다.이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.