Android Studio 使用 Gradle 打包 Jar

pm45e 9年前

Android Studio 打 Jar 包一直是一个麻烦的事,按照网上现有的教程,打包一个混淆的 jar 需要完成下列步骤:

  1. 将 plugin 修改为library后 build 出 aar,再提取 aar 里面的 classes.jar
  2. 使用 jarjar 等工具剔除多余的 class
  3. 对第二步得到的 jar 进行混淆

无论哪一步,所做的工作量都不少。于我个人而言,相当麻烦,于是花了些时间研究了下 Gradle 打 Jar 包。

代码

废话不多说,先上代码( :只在 Gradle Android Plugin 1.2.3 测试过)

build.gradle
 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 中已注册的组件的规则文件。然而我们的 task 只是依赖于compileReleaseJava(该 task 在执行 aapt 前), Gradle Android Plugin 中配置上述 aapt 生成的规则文件的代码如下:

BasePlugin.groovy
 protected File createProguardTasks(@NonNull BaseVariantData variantData,                                         @Nullable BaseVariantData testedVariantData) {      ......    // also the config file output by aapt    proguardTask.configuration(variantData.processResourcesTask.proguardOutputFile)    ......  }

碍于技术原因,获取不到processResourcesTask的实例,所以目前只能先添加对应的组件到规则文件中,还望知道怎么获取的朋友能够分享下,谢谢。

使用方法

不需要混淆则运行命令

gradle buildJar  或  ./gradlew buildjar

需要混淆则运行

gradle proguardJar  或  ./gradlew proguardJar

总结

buildJar这部分相对比较简单,很多内容网上都有教程。关键在于混淆,由于需要导入 runtime 相关的 jar,虽说可以写死 runtime 的路径,但是团队每个人都有自己的安装习惯,路径不一定一致,于是乎看源码翻了一段时间才找到相应的代码。至于想更多个性化的朋友,建议从源码入手。