最近研究了一下安卓插件的开发,就以开发一个360加固自动打包插件为例,练了一下,本次使用android studio基于kotlin构建自动打包插件.
添加依赖
构建插件需要使用gradle依赖,build.gradle添加以下内容
apply plugin: 'java-library'
apply plugin: 'kotlin'
apply plugin: 'groovy'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation gradleApi()
implementation localGroovy()
implementation 'com.android.tools.build:gradle:3.6.3'
}
tasks.withType(JavaCompile) {
options.encoding = "UTF-8"
}
sourceCompatibility = "1.8"
targetCompatibility = "1.8"
//设置maven deployer,发布到指定目录
apply plugin: 'maven'
repositories {
mavenCentral()
}
uploadArchives {
repositories {
mavenDeployer {
//设置插件的GAV参数
pom.groupId = 'com.mml.jiagu.plugin'
pom.artifactId = '360'
pom.version = '1.0.0'
//文件发布到此模块repo下面目录
repository(url: uri('repo'))
}
}
}
//发布到本地仓库
apply plugin: 'maven-publish'
publishing{
publications{
JiaGuPlugin(MavenPublication){
from components.java
groupId 'com.mml.jiagu.plugin'
artifactId '360'
version '1.1'
}
}
}
创建一个配置扩展类
用于存储360加固配置
使用kotlin创建的bean 不能使用data class 创建,因为是final类型的,在实例化时将不能读取,故创建一个开放的普通类
package com.mml.jiagu.plugin
open class JiaGuPluginExtension {
var jiaGuToolsPath: String? = null
var username: String? = null
var password: String? = null
var keyStoreFile: String? = null
var keyStorePassword: String? = null
var keyAlias: String? = null
var keyAliasPassword: String? = null
var channelFile: String? = null
var inputFile: String? = null
var outputFile: String? = null
var config:Array<String>? = null
}
创建 task
创建执行的任务具体内容
package com.mml.jiagu.plugin
import org.gradle.api.DefaultTask
import org.gradle.api.tasks.TaskAction
import java.io.*
import javax.inject.Inject
/**
* Author: Menglong Ma
* Email: [email protected]
* Date: 2020/4/24 18:30
* Description: This is JiaGuTask
* Package: com.mml.jiagu.plugin
* Project: DeskTopPhotoView
*/
//使用kotlin 需要声明类open
open class JiaGuTask() : DefaultTask() {
lateinit var apk: File
lateinit var jiaGuPluginExtension: JiaGuPluginExtension
//@Inject task的入口
@Inject
constructor(apk: File, jiaGuPluginExtension: JiaGuPluginExtension) : this() {
group = JiaGuPlugin.GROUP
description = " 360 jiagu"
this.apk = apk
this.jiaGuPluginExtension = jiaGuPluginExtension
}
// @TaskAction 标注,双击执行这个任务时调用这个方法
@TaskAction
fun begin() {
//参照360加固文档编写相应的命令
println("login 360 account")
project.exec { execSpec ->
execSpec.commandLine(
"java", "-jar", jiaGuPluginExtension.jiaGuToolsPath,
"-login", jiaGuPluginExtension.username, jiaGuPluginExtension.password
)
}
println("import sign")
project.exec { execSpec ->
execSpec.commandLine(
"java",
"-jar",
jiaGuPluginExtension.jiaGuToolsPath,
"-importsign",
jiaGuPluginExtension.keyStoreFile,
jiaGuPluginExtension.keyStorePassword
,
jiaGuPluginExtension.keyAlias,
jiaGuPluginExtension.keyAliasPassword
)
}
jiaGuPluginExtension.channelFile?.let {
println("import mulpkg")
project.exec { execSpec ->
execSpec.commandLine(
"java", "-jar", jiaGuPluginExtension.jiaGuToolsPath,
"-importmulpkg", it
)
}
}
jiaGuPluginExtension.config?.let {
println("config ")
project.exec { execSpec ->
execSpec.commandLine(
"java", "-jar", jiaGuPluginExtension.jiaGuToolsPath,
"--config", it
)
}
}
println("origin apk.absolutePath:${apk.absolutePath}")
println("source apk.absolutePath:${apk.parent}")
project.exec { execSpec ->
execSpec.commandLine(
"java", "-jar", jiaGuPluginExtension.jiaGuToolsPath,
"-jiagu",
jiaGuPluginExtension.inputFile ?: apk.absolutePath,
jiaGuPluginExtension.outputFile ?: apk.parent, "-autosign",
"${jiaGuPluginExtension.channelFile?.let { "-automulpkg" }}"
)
}
}
}
创建plugin
继承 Plugin 接口,实现apply方法
main目录下创建插件配置,路径没有就创建,并且不可更改resources/META-INF/gradle-plugins/com.mml.jiagu.plugin.properties
,其中com.mml.jiagu.plugin
表示应用时的名字,如:apply plugin: 'com.mml.jiagu.plugin'
//配置很简单,插件的全类名限定路径
implementation-class=com.mml.jiagu.plugin.JiaGuPlugin
//插件类
package com.mml.jiagu.plugin
import com.android.build.gradle.AppExtension
import org.gradle.api.Plugin
import org.gradle.api.Project
public class JiaGuPlugin : Plugin<Project> {
companion object {
val GROUP = "JiaGu"
//app build.gradle 中使用的名字 jiagu{xxx xxx}
val EXTENSION_NAME = "jiagu"
}
private val taskMap = mutableMapOf<String,String>()
override fun apply(project: Project) {
//读取360加固配置
val jiaGuPluginExtension = project.extensions
.create(EXTENSION_NAME, JiaGuPluginExtension::class.java)
val isResguard = project.plugins.hasPlugin("AndResGuard")
project.afterEvaluate { project ->
println("afterEvaluate ${jiaGuPluginExtension.jiaGuToolsPath}")
val appExtension = project.extensions.getByType(AppExtension::class.java)
appExtension.applicationVariants.all { applicationVariant ->
val flavorName= applicationVariant.flavorName
applicationVariant.outputs.all {
val outputFile = it.outputFile
//println("outputFile:${outputFile.absolutePath}")
val jiaGuName = "jiagu-${it.name}"
println("create task:$jiaGuName")
val jiaGuTask = project.tasks.create(
jiaGuName,
JiaGuTask::class.java,
outputFile,
jiaGuPluginExtension
)
val buildAndJiaGuName = "build-and-$jiaGuName"
println("create task:$buildAndJiaGuName")
taskMap[buildAndJiaGuName] = "assemble$flavorName"
val buildAndJiaGuTask = project.tasks.create(
buildAndJiaGuName,
JiaGuTask::class.java,
outputFile,
jiaGuPluginExtension
)
buildAndJiaGuTask.doFirst {
println("${it.name} doFirst")
}
buildAndJiaGuTask.doLast {
println("${it.name} doLast")
}
buildAndJiaGuTask.dependsOn("assemble$flavorName")
}
}
}
project.tasks.whenTaskAdded {task ->
println("whenTaskAdded:${task.name} dependsOn ${task.dependsOn}")
val get=taskMap[task.name]
get?.let {
println("task ${task.name} dependsOn $it")
task.dependsOn(it)
}
}
}
}
发布本地仓库
编写完成插件,可在gradle命令集中发现以下命令publishing和upload,分别对应build.gradle里的两个发布本地任务,执行gradle 命令发布本地.
项目引用
发布本地成功以后,即可进行使用,在project build.gradle中引用
repositories {
google()
jcenter()
//本地仓库
mavenLocal()
//指定仓库
maven {
url uri("JiaGuPlugin/repo")
}
}
dependencies {
classpath 'com.android.tools.build:gradle:3.6.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
//引用相应仓库中的包
// classpath 'com.mml.jiagu.plugin:360:1.0.0'
classpath 'com.mml.jiagu.plugin:360:1.1'
}
在app build.gradle中使用插件
添加以下带,构建完成以后会在项目任务集中发现jiagu任务
apply plugin: 'com.mml.jiagu.plugin'
String os = "${project.rootDir.absolutePath}/BaseWheelsByKotlin/JiaGuPlugin/tools/${getJiaguJarFilePathByOs()}"
jiagu {
jiaGuToolsPath "${os}/jiagu.jar"
username "XXX"
password "XXX"
keyStoreFile android.signingConfigs.release.storeFile.absolutePath
keyStorePassword android.signingConfigs.release.storePassword
keyAlias android.signingConfigs.release.keyAlias
keyAliasPassword android.signingConfigs.release.keyPassword
inputFile //"${buildDir.getAbsolutePath()}\\outputs\\apk\\release\\app-release.apk"
outputFile //"${buildDir.getAbsolutePath()}\\jiagu" //这里指定的是输出文件夹
config '-so', '-data', '-assets', '-string_obfus', '-so_private'
channelFile "${os}/多渠道模板.txt"
}
String getJiaguJarFilePathByOs(){
String osName = org.gradle.internal.os.OperatingSystem.current().getName();
String osVersion = org.gradle.internal.os.OperatingSystem.current().getVersion();
println "*** $osName $osVersion was detected."
if (org.gradle.internal.os.OperatingSystem.current().isLinux()) {
println("Linux")
return "linux"+File.separator+"jiagu"
} else if (org.gradle.internal.os.OperatingSystem.current().isWindows()) {
println("Windows")
return "windows"+File.separator+"jiagu"
} else if (org.gradle.internal.os.OperatingSystem.current().isMacOsX()) {
println("OSX")
return "mac"+File.separator+"jiagu"
}
}