Android Studio 打 Jar 包一直是一个麻烦的事,按照网上现有的教程,打包一个混淆的 jar 需要完成下列步骤:
- 将 plugin 修改为
library
后 build 出 aar,再提取 aar 里面的 classes.jar - 使用 jarjar 等工具剔除多余的 class
- 对第二步得到的 jar 进行混淆
无论哪一步,所做的工作量都不少。于我个人而言,相当麻烦,于是花了些时间研究了下 Gradle 打 Jar 包。
代码
废话不多说,先上代码(注:只在 Gradle Android Plugin 1.2.3 测试过)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
import com.android.build.gradle.AppPlugin import proguard.gradle.ProGuardTask apply plugin: 'com.android.application' android { compileSdkVersion 22 buildToolsVersion "22.0.1" defaultConfig { applicationId "org.chaos.demo.jar" minSdkVersion 22 targetSdkVersion 22 versionCode 1 versionName "1.0" } buildTypes { release { minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) } //dependsOn 可根据实际需要增加或更改 task buildJar(dependsOn: ['compileReleaseJava'], type: Jar) { appendix = "demo" baseName = "androidJar" version = "1.0.0" classifier = "release" //后缀名 extension = "jar" //最终的 Jar 包名,如果没设置,默认为 [baseName]-[appendix]-[version]-[classifier].[extension] archiveName = "AndroidJarDemo.jar" //需打包的资源所在的路径集 def srcClassDir = [project.buildDir.absolutePath + "/intermediates/classes/release"]; //初始化资源路径集 from srcClassDir //去除路径集下部分的资源 // exclude "org/chaos/demo/jar/MainActivity.class" // exclude "org/chaos/demo/jar/MainActivity\$*.class" exclude "org/chaos/demo/jar/BuildConfig.class" exclude "org/chaos/demo/jar/BuildConfig\$*.class" exclude "**/R.class" exclude "**/R\$*.class" //只导入资源路径集下的部分资源 include "org/chaos/demo/jar/**/*.class" //注: exclude include 支持可变长参数 } task proguardJar(dependsOn: ['buildJar'], type: ProGuardTask) { //Android 默认的 proguard 文件 configuration android.getDefaultProguardFile('proguard-android.txt') //会根据该文件对 Jar 进行混淆,注意:需要在 manifest 注册的组件也要加入该文件中 configuration 'proguard-rules.pro' String inJar = buildJar.archivePath.getAbsolutePath() //输入 jar injars inJar //输出 jar outjars inJar.substring(0, inJar.lastIndexOf('/')) + "/proguard-${buildJar.archiveName}" //设置不删除未引用的资源(类,方法等) dontshrink AppPlugin appPlugin = getPlugins().findPlugin(AppPlugin) if (appPlugin != null) { List<String> runtimeJarList if (appPlugin.getMetaClass().getMetaMethod("getRuntimeJarList")) { runtimeJarList = appPlugin.getRuntimeJarList() } else if (android.getMetaClass().getMetaMethod("getBootClasspath")) { runtimeJarList = android.getBootClasspath() } else { runtimeJarList = appPlugin.getBootClasspath() } for (String runtimeJar : runtimeJarList) { //给 proguard 添加 runtime libraryjars(runtimeJar) } } } |
为什么已在 manifest 注册的组件需要在 .pro 文件声明对应的混淆规则?
可能各位注意到 proguardJar task 的第二行注释,在 apk 的打包过程中,aapt 会在解析 manifest 后生成一个用于不混淆 manifest 中已注册的组件的规则文件。Gradle Android Plugin 中配置上述 aapt 生成的规则文件的代码如下:
1 2 3 4 5 6 7 |
protected File createProguardTasks(@NonNull BaseVariantData variantData, @Nullable BaseVariantData testedVariantData) { ...... // also the config file output by aapt proguardTask.configuration(variantData.processResourcesTask.proguardOutputFile) ...... } |
碍于个人能力原因,获取不到 processResourcesTask
的实例,所以目前只能先添加对应的组件到规则文件中,还望知道怎么获取的朋友能够分享下,谢谢。
使用方法
不需要混淆则运行命令
1 2 3 4 |
gradle buildJar 或 ./gradlew buildjar |
需要混淆则运行
1 2 3 4 |
gradle proguardJar 或 ./gradlew proguardJar |
最后
buildJar 这部分相对比较简单,很多内容网上都有教程。关键在于混淆,由于团队每个人都有自己的安装习惯,JDK、Android SDK 路径不一定一致,并不能直接写死 runtime 的路径,最后直接看 Android Plugin 源码才写出了 proguardJar task。
至于想更多个性化的朋友,建议从源码入手。