github地址 https://github.com/mikaelzero/KotlinJunk

前言

本库参考自 https://github.com/qq549631030/AndroidJunkCode 的实现,主要区别在于本库生成的是kotlin的代码,并且各个资源的名称随机性高,基于Bosster进行开发插件,方便集成

实现的效果

在打包之后会出现大量的垃圾代码,无用代码,来避免某些平台检测代码重复度的时候出现的问题

image.png
image.png
image.png
image.pngimage.png

如何生成垃圾代码

以kotlin为例,通过 kotlinpoet 框架来生成kotlin文件,生成的文件通过IO流保存在项目的某个目录下

drawable的生成只能生成vector,因为vector可以通过代码进行动态调整生成

生成string文件的话,首先要创建 string.xml 文件,然后通过 FileReader 来读取XML文件的节点然后进行数据的插入操作

同理如果是一个Activity,自然要先注册,因此同样是先创建 AndroidManifest 文件,通过 FileReader 来读取XML文件的节点然后进行数据的插入操作

因此生成代码的操作,主要由 kotlinpoet 以及 IO流 来处理

如何将这些代码打包进APK中

Android Studio 按逻辑关系将每个模块的源代码和资源分组为源代码集,在打包阶段,Android Plugin 插件会读取源代码集来判断哪些代码需要打包进入apk,IDE创建项目时会默认设置项目的main/java 为源代码集目录

类似这样的配置

  1. sourceSets {
  2. main {
  3. java {
  4. srcDirs += []
  5. }
  6. kotlin {
  7. srcDirs += ['src/main/kotlin', 'src/main/java']
  8. }
  9. }
  10. test {
  11. java {
  12. srcDirs += []
  13. }
  14. kotlin {
  15. srcDirs += ['src/main/kotlin', 'src/main/java']
  16. }
  17. }
  18. }

如果你想让某个目录的类或者资源能够打包进APK,你需要在sourceSets中进行add的配置,由于我们实现的是一个插件,因此我们需要自己把之前生成的目录,自动添加到 srcDirs 中

  1. val generatedResFolders = variant.project.files(resDir).builtBy(it)
  2. variant.registerGeneratedResFolders(generatedResFolders)
  3. variant.registerJavaGeneratingTask(it, javaDir)

registerGeneratedResFolders 的官网注释为 Adds to the variant new generated resource folders,也就代表着我们可以将自己生成的路径注册进去,registerJavaGeneratingTask也是同理

另外还有清单配置文件的合并问题,一段官方描述如下:

image.png

可以看出,如果我们没有设置变体和产品变种,默认为两种源代码集,一个为 src/debug/ ,一个为 src/main/

在BaseVariant的sourceSets中可以获取到对应的两个SourceProvider,通过SourceProvider的 getManifestFile 可以获取到绝对路径,这个路径有可能不存在内容,因此我们可以在不存在的情况下,把我们自己生成的清单文件路径赋值给SourceProvider

  1. variant.sourceSets.forEach { sourceSet ->
  2. if (!sourceSet.manifestFile.exists()) {
  3. android.sourceSets.maybeCreate(sourceSet.name).manifest.srcFile(manifestFile.absolutePath)
  4. return@forEach
  5. }
  6. }

Kotin代码

考虑到生成的是kotlin代码,在registerJavaGeneratingTask有备注支持kotlin生成,但是随着版本更新可能移除,因此需要选择另外一个方式来将kotlin的类打入包中

  1. val kotlinCompileTask = tasks.findByName("compile${variant.name.capitalize()}Kotlin") as? SourceTask
  2. if (kotlinCompileTask != null) {
  3. kotlinCompileTask.dependsOn(it)
  4. val srcSet = variant.project.objects.sourceDirectorySet("kotlin", "kotlin").srcDir(javaDir)
  5. kotlinCompileTask.source(srcSet)
  6. }

总结

  1. 如何生成代码,主要是kotlinpoet或者javapoet以及IO流来操作文件
  2. 如何打包入APK,主要是通过sourceSets的动态添加