AGP7.0|kts 搞一个加固插件

829 阅读2分钟

每次都要手动使用工具去加固,非常麻烦,所以自己搞一个加固插件来提高生产力

开发环境使用的AGP7.0.2 ,相比较之前的版本,改动还是蛮大的,自己也踩了不少坑。

更多AGP7.0的内容可以关注一下虾哥的这篇文章

juejin.cn/post/705639…

我们分为以下几步去完成:

  1. 获取apk产物
  2. 获取签名
  3. 获取加固工具
  4. 进行加固

获取APK

AGP7获取apk 的方式跟以前也大不相同了 ,我们需要借助Variant API apk 来进行获取

Variant API 是 Android Gradle 插件中的扩展机制,可以操纵各种选项,这些选项通常使用可影响 Android build 的 build 配置文件中的 DSL 进行设置。还可以通过 Variant API 访问 build 期间创建的中间工件和最终工件,例如类文件、合并后的清单或 APK/AAB 文件。

这里贴一下官方的示例

github.com/android/gra…

AGP引入了 AndroidComponentsExtension ,可以为 finalizeDsl()beforeVariants()onVariants() 注册回调

  1. finalizeDsl:您可以通过此回调在 DSL 对象因组件(变体)创建而被锁定之前对其进行更改。VariantBuilder 对象是基于 DSL 对象中包含的数据创建的。
  2. beforeVariants此回调可通过 VariantBuilder影响系统会创建哪些组件以及所创建组件的部分属性。它还支持对 build 流程和生成的工件进行修改。
  3. onVariants:在此回调中,您可以访问已创建的 Variant 对象,您还可以为它们包含的 Property 值设置值或提供程序,以进行延迟计算。

在我们自定义的插件里面 获取到 AndroidComponentsExtension,通过 AndroidComponentsExtension 的onVariant回调注册 Task

        val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java)
        androidComponents.onVariants {
            project.tasks.register<ApkTask>("${it.name}DisplayApks") {
                apkFolder.set(it.artifacts.get(SingleArtifact.APK))
                builtArtifactsLoader.set(it.artifacts.getBuiltArtifactsLoader())              
            }
        }

接下来看我们自定义的Task , 这些代码 在官方代码中都有实例

abstract class ApkTask : DefaultTask() {
    @get:InputFiles
    abstract val apkFolder: DirectoryProperty

    @get:Internal
    abstract val builtArtifactsLoader: Property<BuiltArtifactsLoader>

    @TaskAction
    fun taskAction() {
        val builtArtifacts = builtArtifactsLoader.get().load(apkFolder.get()) 
           ?: throw RuntimeException("Cannot load APKs")
        builtArtifacts.elements.forEach {
            val apk = File(it.outputFile)
        }
    }
}

task的class 要是abstract 类型的,否则编译会提示final类型的错误。

获取签名

获取签名方面,我并没有在官方实例中找到资料,是在源码中发现了点踪迹。

先看下我们app下 build.gradle.kts中签名的配置

android {
    signingConfigs {
        create("release") {
            storeFile = file("enjoy.keystore")
            storePassword = "123456"
            keyAlias = "enjoy"
            keyPassword = "123456"
        }
    }
}

signingConfigs 是一个接口类型的,我们看下实现类:

image.png

多个实现类我们并不知道找哪个,接着往上一层看,下面的android 方法 的源码

fun org.gradle.api.Project.`android`(configure: Action<com.android.build.gradle.internal.dsl.BaseAppModuleExtension>): Unit =
    (this as org.gradle.api.plugins.ExtensionAware).extensions.configure("android", configure)

可以看到它使用的是BaseAppModuleExtension,那我们获取到BaseAppModuleExtension 是不是就可以拿到签名了 ,在我尝试了一下果然可以 。

 val baseAppModuleExtension = project.extensions.getByType(BaseAppModuleExtension::class.java)
 val signingConfig = baseAppModuleExtension.signingConfigs.getByName("release")

这样就拿到了签名

获取加固工具

加固工具,我们是希望可以在app的gradle中可以动态配置 ,这里也是参考官方的方案:

github.com/gradle/kotl…

先创建一个实体类,同样是要抽象类,需要的是工具地址,用户名和密码

abstract class JiaguExtension {
    abstract val jarPath: Property<String>
    abstract val username: Property<String>
    abstract val pwd: Property<String>
}

然后在插件中进行获取

val jiaguExtension  = project.extensions.create<JiaguExtension>("jiagu")

然后我们需要将配置信息传到task中进行加固任务。

进行加固

最后一步,我们开始进行加固 ,这里先介绍一下加固工具的使用:

image.png

然后我们通过project提供的commandLine方法执行加固命令

 project.exec {
      登录 
      commandLine("java", "-jar", jiagu.jarPath.get(), "-login", jiagu.username.get(), jiagu.pwd.get())                
      配置签名         
      commandLine("java", "-jar", jiagu.jarPath.get(), "-importsign", signingConfig.storeFile?.absolutePath,
                    signingConfig.storePassword, signingConfig.keyAlias, signingConfig.keyPassword)
      进行加固                          
      commandLine("java", "-jar", jiagu.jarPath.get(), "-jiagu", apk.absolutePath, apk.parent, "-autosign")
}

至此就完成了插件的加固功能。

使用

最后我们使用 maven-publish 插件推到本地进行引入使用

我们先需要在插件moudle的gradle中进行插件配置

gradlePlugin{
    plugins {
        register("jiaguPlugin") {
            id = "jiagu-plugin"
            implementationClass = "jiagu.JiaguPlugin"
        }
    }
}

这个id 是我们引入的时候所用的id,implementationClass 指向的是我们自定义的Plugin

最后我们发布到自己的本地仓库

group = "jiagu"
version = "1.0"
publishing {
    repositories {
        maven(url = "../repository")
    }
}

然后就是正常使用引入插件:

Project 的build.gradle.kts中:

 repositories {
            maven { url = uri("./repository/") }
        }
 classpath("jiagu:jiagu-plugin:1.0")       

app 的build.gradle.kts中:

plugins {
    id("com.android.application")
    id("kotlin-android")
    id("jiagu-plugin")
}

jiagu{
    jarPath.set("/Users/wyl/Downloads/360jiagubao_mac/jiagu/jiagu.jar")
    username.set("182XXXX0810")
    pwd.set("xiao")
}

最后可以看到在Gradle中看到加固插件已经存在了

image.png

最后看下效果:

image.png

其他

除此之外,如果想在assembleDebug或者 assembleDebug 中插入加固task,可以采用这种方案

   val apkProducer = project.tasks.register("apk${it.name}Task", ApkTask2::class.java)
            it.artifacts.use(apkProducer).wiredWithDirectories(
                ApkTask2::inputApk,
                ApkTask2::outputApk
            ).toTransform(SingleArtifact.APK)
abstract class ApkTask2 : DefaultTask() {
    @get:InputDirectory
    abstract val inputApk: DirectoryProperty

    @get:OutputDirectories
    abstract val outputApk: DirectoryProperty
    @TaskAction
    fun taskAction() {
        val apks = File(inputApk.get().toString())
        apks.listFiles()?.forEach {
            if (it.name.endsWith(".apk")) {
                val outPutFile = File(outputApk.get().toString() + "/out.apk")
                //执行加固任务
            }
        }
    }
}

这样每次执行assembleDebug或者 assembleDebug 任务的时候都会进行apk的加固。

结尾

至此完结撒花。

欢迎大家一起交流分享,提出宝贵意见。

喜欢的朋友可以关注公众号:Android开发那点事儿

参考:

虾哥:juejin.cn/post/705639…

官方文档:github.com/gradle/kotl…

官方文档:github.com/android/gra…

官方文档:developer.android.google.cn/studio/buil…