1) 前言
ProGuard是一個(gè)開源的Java代碼混淆器。它可以混淆Android項(xiàng)目里面的java代碼,對(duì)的,你沒(méi)看錯(cuò),僅僅是java代碼。它是無(wú)法混淆Native代碼,資源文件drawable、xml等。
2) ProGuard作用
壓縮: 移除無(wú)效的類、屬性、方法等
優(yōu)化: 優(yōu)化字節(jié)碼,并刪除未使用的結(jié)構(gòu)
混淆: 將類名、屬性名、方法名混淆為難以讀懂的字母,比如a,b,c
3) 混淆注意事項(xiàng)
1. 不能混淆
在AndroidManifest中配置的類,比如四大組件
JNI調(diào)用的方法
反射用到的類
WebView中JavaScript調(diào)用的方法
Layout文件引用到的自定義View
-
一些引入的第三方庫(kù)(一般都會(huì)有混淆說(shuō)明的)
這里推薦兩個(gè)開源項(xiàng)目,里面收集了一些第三方庫(kù)的混淆規(guī)則
不難理解,混淆之后,類名會(huì)變成a,b,c這種,通過(guò)包名+類名自然就會(huì)找不到該類了,自然就會(huì)出現(xiàn)ClassNotFoundException異常。這里推薦一篇文章:
http://www.itnose.net/detail/6043297.html
2. Log處理
我們都知道,使用Log的時(shí)候,需要用到TAG,然而TAG我們一般都會(huì)寫成:
private static final String TAG = MainActivity.class.getSimpleName()
這時(shí)候MainActivity如何被混淆的話,log輸出信息就會(huì)變成V/a:xxxxxxx,所以為了讓log輸出信息維持原狀,可以將TAG處理成固定的字符串:
private static final String TAG = "MainActivity"
正好Android Studio里面的Live Templates
[圖片上傳失敗...(image-b36df1-1513066616611)]
能讓你輕輕松松的聲明TAG
[圖片上傳失敗...(image-acf1b7-1513066616610)]
關(guān)于Log處理,推薦一篇文章:https://www.zybuluo.com/shark0017/note/163330
3. Crash信息處理
代碼混淆的時(shí)候記得加上在混淆文件里面記得加上這句:
# keep住源文件以及行號(hào)
-keepattributes SourceFile,LineNumberTable
否則你看到的崩潰信息就會(huì)變成這樣子(圖片來(lái)自bugly)
[圖片上傳失敗...(image-be7db8-1513066616610)]
這里推薦bugly的一篇文章:
http://bugly.qq.com/bbs/forum.php?mod=viewthread&tid=26&extra=page%3D1
4)ProGuard使用
1. 常用語(yǔ)法
保留
-keep {Modifier} {class_specification} 保護(hù)指定的類文件和類的成員
-keepclassmembers {modifier} {class_specification} 保護(hù)指定類的成員,如果此類受到保護(hù)他們會(huì)保護(hù)的更好
-keepclasseswithmembers {class_specification} 保護(hù)指定的類和類的成員,但條件是所有指定的類和類成員是要存在。
-keepnames {class_specification} 保護(hù)指定的類和類的成員的名稱(如果他們不會(huì)壓縮步驟中刪除)
-keepclassmembernames {class_specification} 保護(hù)指定的類的成員的名稱(如果他們不會(huì)壓縮步驟中刪除)
-keepclasseswithmembernames {class_specification} 保護(hù)指定的類和類的成員的名稱,如果所有指定的類成員出席(在壓縮步驟之后)
-printseeds {filename} 列出類和類的成員-keep選項(xiàng)的清單,標(biāo)準(zhǔn)輸出到給定的文件
壓縮
-dontshrink 不壓縮輸入的類文件
-printusage {filename}
-whyareyoukeeping {class_specification}
優(yōu)化
-dontoptimize 不優(yōu)化輸入的類文件
-assumenosideeffects {class_specification} 優(yōu)化時(shí)假設(shè)指定的方法,沒(méi)有任何副作用
-allowaccessmodification 優(yōu)化時(shí)允許訪問(wèn)并修改有修飾符的類和類的成員
混淆
-dontobfuscate 不混淆輸入的類文件
-obfuscationdictionary {filename} 使用給定文件中的關(guān)鍵字作為要混淆方法的名稱
-overloadaggressively 混淆時(shí)應(yīng)用侵入式重載
-useuniqueclassmembernames 確定統(tǒng)一的混淆類的成員名稱來(lái)增加混淆
-flattenpackagehierarchy {package_name} 重新包裝所有重命名的包并放在給定的單一包中
-repackageclass {package_name} 重新包裝所有重命名的類文件中放在給定的單一包中
-dontusemixedcaseclassnames 混淆時(shí)不會(huì)產(chǎn)生形形色色的類名
-keepattributes {attribute_name,…} 保護(hù)給定的可選屬性,例如LineNumberTable, LocalVariableTable, SourceFile, Deprecated, Synthetic, Signature, and InnerClasses.
-renamesourcefileattribute {string} 設(shè)置源文件中給定的字符串常量
通配符匹配規(guī)則
通配符 | 規(guī)則 |
---|---|
? | 匹配單個(gè)字符 |
* | 匹配類名中的任何部分,但不包含額外的包名 |
** | 匹配類名中的任何部分,并且可以包含額外的包名 |
% | 匹配任何基礎(chǔ)類型的類型名 |
*** | 匹配任意類型名 ,包含基礎(chǔ)類型/非基礎(chǔ)類型 |
... | 匹配任意數(shù)量、任意類型的參數(shù) |
<init> | 匹配任何構(gòu)造器 |
<ifield> | 匹配任何字段名 |
<imethod> | 匹配任何方法 |
*(當(dāng)用在類內(nèi)部時(shí)) | 匹配任何字段和方法 |
$ | 指內(nèi)部類 |
更詳細(xì)的語(yǔ)法請(qǐng)戳:http://proguard.sourceforge.net/manual/usage.html#classspecification
2. Android Studio中使用方法
按照上面的語(yǔ)法規(guī)則編寫proguard-rules.pro后,需要在build.gradle中配置,需要混淆的時(shí)候,設(shè)置minifyEnabled為true即可
buildTypes {
debug {
minifyEnabled false
}
release {
signingConfig signingConfigs.release
minifyEnabled true
proguardFiles 'proguard-rules.pro'
}
}
3. ProGuard的輸出文件說(shuō)明
混淆后,會(huì)在/build/proguard/目錄下輸出下面的文件
dump.txt 描述apk文件中所有類文件間的內(nèi)部結(jié)構(gòu)。
mapping.txt 列出了原始的類,方法,和字段名與混淆后代碼之間的映射。
seeds.txt 列出了未被混淆的類和成員
-
usage.txt 列出了從apk中刪除的代碼
當(dāng)我們需要處理crash log的時(shí)候,就可以通過(guò)mapping.txt的映射關(guān)系找到對(duì)應(yīng)的類,方法,字段等。方法如下:
sdk\tools\proguard\bin 目錄下有個(gè)retrace工具可以將混淆后的報(bào)錯(cuò)堆棧解碼成正常的類名
window下為retrace.bat,linux和mac為retrace.sh,
使用方法如下:
將crash log保存為yourfilename.txt
拿到版本發(fā)布時(shí)生成的mapping.txt
執(zhí)行命令retrace.bat -verbose mapping.txt yourfilename.txt
所以我們每次打包版本都需要保存最新的mapping.txt文件。如果要使用到第三方的crash統(tǒng)計(jì)平臺(tái),比如bugly,還需要我們上傳APP版本對(duì)應(yīng)的mapping.txt.每次都要保存最新的mapping文件,那不就很麻煩?放心,gradle會(huì)幫到你,只需要在bulid.gradle加入下面的一句。每次我們編譯的時(shí)候,都會(huì)自動(dòng)幫你保存mapping文件到本地的。
android {
applicationVariants.all { variant ->
variant.outputs.each { output ->
if (variant.getBuildType().isMinifyEnabled()) {
variant.assemble.doLast{
copy {
from variant.mappingFile
into "${projectDir}/mappings"
rename { String fileName ->
"mapping-${variant.name}.txt"
}
}
}
}
}
......
}
}
5) 參考
https://blog.gmem.cc/proguard-study-note
http://developer.android.com/intl/zh-cn/tools/help/proguard.html