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