The introduction
In the previous stage, we learned the design concept and concrete implementation of sample-Manager module:
(1) 【 Plug-in & Hotfix series 】Shadow source code parsing sample-Manager (一)
(2) 【 Plug-in & Hotfix series 】Shadow source code parsing sample-Manager (2)
At this point, our hotfix has completed the Host and Admin Plugin modules
The next stage is plug-in modules, such as:
- How are multiple business plug-in production processes distributed as zip?
- How is each plug-in written?
- How are the components in the plug-in supported?
- .
Let’s start our new journey with how to zip the Plug-in Production Process
The profile
This is the content of the Shadow plugin zip package. There are 4 plug-ins and 1 configuration file
The configuration file above shows some version information of the plug-in
This chapter will learn how to generate this information using the Gradle plugin
The implementation process
1. Achieve your purpose
The main implementation of this project is to package the sample-manager.apk as zip
2. Effect display
Perform the packing taskThe zip package is then produced to the specified directory
3. The plugin defines/uploads/uses the gradle configuration
(1) Plug-in definition
The definition used here is the new version of the definition, not quite the same as before
The new method is called Composing builds, while the old one is called buildSrc
The main differences are:
- BuildSrc: A change in buildSrc causes the whole project to become out-of-date. BuildSrc: A change in buildSrc causes the whole project to become out-of-date.
- Composing builds, A composite build is simply a build that includes other builds. In many ways a composite build is similar to a Gradle multi-project build, except that instead of including single projects, Complete builds are included. In many ways, composite builds are similar to Gradle multi-project builds, except that they include full builds instead of individual projects and feature faster builds.
In addition to the official documents, there are also more good online introduction, such as byte students share
(2) Plug-in upload Here, a relatively simple upload method is used to upload to the local project’s REPO
(3) Plug-in introduction
(4) Use of plug-ins
The configuration we use is to package the APK output from sample-Manager into zip
4. Project Introduction
This is the effect of combining the official code, the original official structure is as follows:
5. Source code analysis
(1) Find the local androidJar and customize the classPool to complete the disable_SHADOW_transform switch
Because the switch function is not the focus of this chapter, so the implementation of temporary annotations, to understand its code about the role
(2) Custom expansion
ShadowExtension, is an implementation of a collection
open class ShadowExtension {
var transformConfig = TransformConfig()
fun transform(action: Action<in TransformConfig>) {
action.execute(transformConfig)
}
}
class TransformConfig {
var useHostContext: Array<String> = emptyArray()
}
Copy the code
PackagePluginExtension looks like this:
open class PackagePluginExtension {
var loaderApkProjectPath = ""
var runtimeApkProjectPath = ""
var archivePrefix = ""
var archiveSuffix = ""
var destinationDir = ""
var uuid = ""
var version: Int = 0
var uuidNickName = ""
var compactVersion: Array<Int> = emptyArray()
var buildTypes: NamedDomainObjectContainer<PluginBuildType>
}
Copy the code
The PluginBuildType inside is as follows:
open class PluginBuildType {
var name = ""
var loaderApkConfig: Tuple2<String, String> = Tuple2(""."")
var runtimeApkConfig: Tuple2<String, String> = Tuple2(""."")
lateinit var pluginApks: NamedDomainObjectContainer<PluginApkConfig>
}
Copy the code
PluginApkConfig looks like this:
open class PluginApkConfig {
var name = ""
var partKey = ""
var businessName = ""
var apkName = ""
var apkPath = ""
var buildTask = ""
var dependsOn: Array<String> = emptyArray()
var hostWhiteList: Array<String> = emptyArray()
}
Copy the code
(3) Task creation
// Create execution tasks based on configured pluginTypes
val tasks = mutableListOf<Task>()
for (i in buildTypes) {
// Organize plug-ins such as APk and JSON as zip
val task = createPackagePluginTask(project, i)
tasks.add(task)
}
Copy the code
Build tasks according to buildTypes
BuildTypes is the PluginBuildType above, and this example has only one debug type
Then let’s look at the implementation of createPackagePluginTask:
internal fun createPackagePluginTask(project: Project, buildType: PluginBuildType): Task {
/** ** directory ** /
val targetConfigFile = File(project.projectDir.absolutePath + "/generatePluginConfig/${buildType.name}/config.json")
/** * 1) rely on createGenerateConfigTask * 2) zip package ** /
return project.tasks.create("package${buildType.name.capitalize()}Plugin", Zip::class.java) {
System.err.println("PackagePluginTask task start run...")
//runtime apk file
val runtimeApkName: String = buildType.runtimeApkConfig.first
var runtimeFile: File? = null
if (runtimeApkName.isNotEmpty()) {
System.err.println("runtime apk...")
runtimeFile = ShadowPluginHelper.getRuntimeApkFile(project, buildType, false)}//loader apk file
val loaderApkName: String = buildType.loaderApkConfig.first
var loaderFile: File? = null
if (loaderApkName.isNotEmpty()) {
System.err.println("loader apk...")
loaderFile = ShadowPluginHelper.getLoaderApkFile(project, buildType, false)}//config file
/ / does not exist: / Users/yabber AndroidStudioProjects/DemoHot/sample - the plugin - app/build + / intermediates
//val targetConfigFile = File(project.buildDir.absolutePath + "/intermediates/generatePluginConfig/${buildType.name}/config.json")
//val targetConfigFile = File(project.projectDir.absolutePath + "/generatePluginConfig/${buildType.name}/config.json")
val result = targetConfigFile.parentFile.mkdirs()
System.err.println(Json parentFile dir, result =" + result)
System.err.println("targetConfigFile = " + targetConfigFile.absoluteFile)
//all plugin apks
val pluginFiles: MutableList<File> = mutableListOf()
for (i in buildType.pluginApks) {
val file = ShadowPluginHelper.getPluginFile(project, i, false)
System.err.println("pluginFile = " + file.absoluteFile)
pluginFiles.add(file)
}
it.group = "plugin"
it.description = "Package plug-ins"
it.outputs.upToDateWhen { false }
if(runtimeFile ! =null) {
pluginFiles.add(runtimeFile)
}
if(loaderFile ! =null) {
pluginFiles.add(loaderFile)
}
it.from(pluginFiles, targetConfigFile)// from
val packagePlugin = project.extensions.findByName("packagePlugin")
val extension = packagePlugin as PackagePluginExtension
val suffix = if (extension.archiveSuffix.isEmpty()) "" else extension.archiveSuffix
val prefix = if (extension.archivePrefix.isEmpty()) "plugin" else extension.archivePrefix
if (suffix.isEmpty()) {
it.archiveName = "$prefix-${buildType.name}.zip"
} else {
it.archiveName = "$prefix-${buildType.name}-$suffix.zip"
}
//destinationDir
it.destinationDir = File(if (extension.destinationDir.isEmpty()) "${project.rootDir}/build" else extension.destinationDir)
System.err.println("destinationDir = " + it.destinationDir.absoluteFile)
System.err.println("PackagePluginTask task end...")
}.dependsOn(createGenerateConfigTask(project, buildType))
}
Copy the code
Here are a few things:
(1) create a task package ${buildType. Name. Capitalize ()} the Plugin
(2) Prepare the plug-in APK File, such as runtime APK/Loader APK/sample-Manager APK/service plug-in APK
(3) Prepare a set of parameters, and then copy the plug-in to the specified directory
(4) Create another task and make it dependent on it
Now let’s see what the other task (createGenerateConfigTask) does.
private fun createGenerateConfigTask(project: Project, buildType: PluginBuildType): Task {
System.err.println("GenerateConfigTask task run ... ")
/** ** directory ** /
val targetConfigFile = File(project.projectDir.absolutePath + "/generatePluginConfig/${buildType.name}/config.json")
val packagePlugin = project.extensions.findByName("packagePlugin")
val extension = packagePlugin as PackagePluginExtension
//runtime apk build task
val runtimeApkName = buildType.runtimeApkConfig.first
var runtimeTask = ""
if (runtimeApkName.isNotEmpty()) {
runtimeTask = buildType.runtimeApkConfig.second
System.err.println("runtime task = $runtimeTask")
//println("runtime task = $runtimeTask")
}
//loader apk build task
val loaderApkName = buildType.loaderApkConfig.first
var loaderTask = ""
if (loaderApkName.isNotEmpty()) {
loaderTask = buildType.loaderApkConfig.second
System.err.println("loader task = $loaderTask")
//println("loader task = $loaderTask")
}
// Plug-in project tasks, such as :sample-manager:assembleDebug
val pluginApkTasks: MutableList<String> = mutableListOf()
for (i in buildType.pluginApks) {
val task = i.buildTask
System.err.println("pluginApkProjects task = $task")
//println("pluginApkProjects task = $task")
pluginApkTasks.add(task)
}
/** * 1) Rely on pluginApkTasks ** /
val task = project.tasks.create("generate${buildType.name.capitalize()}Config") {
it.group = "plugin"
it.description = "Generate plug-in configuration file"
it.outputs.file(targetConfigFile)
it.outputs.upToDateWhen { false }
}
.dependsOn(pluginApkTasks)// Depends on plug-in project tasks such as: : sample-Manager :assembleDebug
.doLast {
System.err.println("generate json Config task begin")
//println("generateConfig task begin")
val json = extension.toJson(project, loaderApkName, runtimeApkName, buildType)
System.err.println("json = " + json)
val bizWriter = BufferedWriter(FileWriter(targetConfigFile))
bizWriter.write(json.toJSONString())
bizWriter.newLine()
bizWriter.flush()
bizWriter.close()
System.err.println("generateConfig task done")}if (loaderTask.isNotEmpty()) {
task.dependsOn(loaderTask)
}
if (runtimeTask.isNotEmpty()) {
task.dependsOn(runtimeTask)
}
return task
}
Copy the code
I mainly did several things:
(1) Parameter preparation, such as reading parameters in the expansion
(2) Create a task, perform json file generation, and execute the task after the specified task (e.g. after sample-Manager :assembleDebug)
At this point, you can see that the task sequence looks something like this:
- First, the plug-in generates apK, such as sample-Manager :assembleDebug
- The json file is then generated based on the generated APK
- Finally, package apK and JSON into ZIP
The source address
Github.com/DaviAndorid…
At the end
Haha, that’s all for this article (systematic learning and growing together)
Tips
Dig into android mobile field, precipitation android application and other technologies, wechat “DaviZgx”, welcome to add to the group communication