Android Studio 打 Jar 包一直是一個麻煩的事,按照網上現有的教程,打包一個混淆的 jar 需要完成下列步驟:
將 plugin 修改爲library後 build 出 aar,再提取 aar 裏面的 classes.jar
使用 jarjar 等工具剔除多餘的 class
對第二步得到的 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()
}elseif(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
protectedFile createProguardTasks(@NonNullBaseVariantData variantData,
@NullableBaseVariantData testedVariantData){
......
// also the config file output by aapt
proguardTask.configuration(variantData.proce***esourcesTask.proguardOutputFile)
......
}
礙於技術原因,獲取不到proce***esourcesTask的實例,所以目前只能先添加對應的組件到規則文件中,還望知道怎麼獲取的朋友能夠分享下,謝謝。
使用方法
不需要混淆則運行命令
gradle buildJar
或
./gradlew buildjar
需要混淆則運行
gradle proguardJar
或
./gradlew proguardJar
總結
buildJar這部分相對比較簡單,很多內容網上都有教程。關鍵在於混淆,由於需要導入 runtime 相關的 jar,雖說可以寫死 runtime 的路徑,但是團隊每個人都有自己的安裝習慣,路徑不一定一致,於是乎看源碼翻了一段時間才找到相應的代碼。至於想更多個性化的朋友,建議從源碼入手。