Proguard是一個代碼優化和混淆工具。能夠提供對Java類文件的壓縮、優化、混淆,和預校驗。壓縮的步驟是檢測並移除未使用的類、字段、方法和屬性。優化的步驟是分析和優化方法的字節碼。混淆的步驟是使用短的毫無意義的名稱重命名剩餘的類、字段和方法。壓縮、優化、混淆使得代碼更小,更高效。
一、Proguard簡單使用:
在build.gradle文件中默認的Proguard文件配置是false關閉的,我們可以在debug模式下打開,設置爲true即可:
默認的getDefaultProguardFile(‘proguard-android.txt’) 會使用build/intermediates/下面的文件(可以println 查看),如果在這個目錄下沒有則可以通過運行gradle任務:生成。而後面的’proguard-rules.pro’則是一個相對路徑,相對於這個build.gradle同級目錄下的proguard-rules.pro
然後在項目的gradle task中雙擊extractProguardFiles任務,就可以看到生成了三個混淆文件:
我們可以將proguard-android文件拷貝到我們項目中,看下系統生成的keep規則使用
可以看到不需要混淆的情況有如下一些:
- native層代碼;
- 所有繼承自view的組件;
- activity裏面的所有view;
- 枚舉;
- Parcelable;
- 靜態內部類;
- webkit接口;
- 所有support包、androidx下面的類;
- 所有使用了keep註解的類、方法、成員變量、構造方法
簡單語法:
-keep 指定類和類成員(變量和方法)不被混淆。
(保護了類名)
-keep class com.dongnao.proxy.guard.test.Bug
(保護了 public static void的沒有參數的函數)
-keep class com.dongnao.proxy.guard.test.Bug{
public static void *();
}
(保護所有)
-keep class com.dongnao.proxy.guard.test.Bug{
*;
}
-keepclassmembers 指定類成員不被混淆(就是-keep的縮小版,不管類名了)。
(都被混淆了)
-keepclassmembers class com.dongnao.proxy.guard.test.Bug
-keepclasseswithmembers 指定類和類成員不被混淆,前提是指定的類成員存在。
(保護類名,但是沒指定成員,所以函數名被混淆)
-keepclasseswithmembers class com.dongnao.proxy.guard.test.Bug
-keepclasseswithmembers class com.dongnao.proxy.guard.test.Bug{
native <methods>;
}
實驗項目地址:https://github.com/buder-cp/base_component_learn/tree/master/performanceOPT/buderdn08
開啓混淆後,我們新建的User類,如果沒有使用到,那麼就不會打包到dex文件中,可以看到並沒有User類:
ProGuard 會移除所有(並且只會移除)未使用的代碼。不過,ProGuard 難以對許多情況進行正確分析,可能會移除應用真正需要的代碼。比如需要反射、動態加載所引用的類等情況,可能因爲proguard移除或者混淆了這部分沒使用的類,而導致錯誤。所以有時需要編寫混淆優化配置文件。在gradle中的proguardFiles 能夠讓我們傳遞File文件或者文件路徑交給proguard來執行。
當我們在MainActivity中使用這個User類後,
User user = new User();
user.age = 18;
user.name = "buder";
並且添加混淆規則,不混淆User類裏面的String字段:
-keep class com.test.buderdn08.User {
java.lang.String *;
}
最終的結果,User類中String成員變量是name,並沒有被混淆掉。
很多混淆命令可以多多嘗試使用。
二、Proguard生成的混淆代碼,如何對應原始代碼
outputs/mapping/debug/mapping.txt文件中保存了混淆前後的對應關係,混淆後的代碼錯誤棧恢復方法:
1.先配置 -keepattributes SourceFile,LineNumberTable
2.把錯誤信息保存到文件
3.使用工具 sdk/tools/groguard/bin/retrace.bat
再執行 retrace.bat -verbose mappint文件 bug文件
我們在Activity中主動寫一個bug,運行產生這樣的日誌:
這個日誌有兩個問題:
- 我們不知道是哪個類報告出的。
- 不知道這個類是哪一行報出的。
第一個問題很顯然是由於我們的代碼進行了混淆。
在開啓混淆後,我們生成apk會產生
dump.txt:
說明 APK 中所有類文件的內部結構。這個文件內容非常多,可讀性也並不高
mapping.txt:
提供原始與混淆過的類、方法和字段名稱之間的轉換。
seeds.txt:
列出未進行混淆的類和成員。我們保護的zip沒有被混淆
usage.txt
:列出從 APK 移除的代碼。可以在這個文件中查看是不是有我們不想被移除的類
最重要的當然就是我們的mapping文件了。通過這個文件我們就能定位到:
出現錯誤的函數是Bug類中的test函數。
我們現在比較簡單,所以一下子就能定位到錯誤函數,如果函數層級較深,光靠自己比對來查找難度就比較大了。所以sdk中提供了一個工具在tools/proguard/bin中。
我們把logcat裏面保存的錯誤堆棧複製到一個文件中:
然後執行retrace 腳本(在 Windows 上爲 retrace.bat;在 Mac/Linux 上爲 retrace.sh)
retrace.bat|retrace.sh [-verbose] mapping.txt [<stacktrace_file>]
現在錯誤日誌一句mapping還原了。所以我們每次在發佈版本之後都需要保留這個mapping文件。所以一般異常上報平臺都會提供mapping上傳然後幫助我們分析。
第一個問題解決的,第二個問題是行數顯示的是Unknow Source
如果希望出現具體行數,我們需要在配置文件中加入
拋出異常時保留代碼行號,在異常分析中可以方便定位
-keepattributes SourceFile,LineNumberTable
另外還可以配合-renamesourcefileattribute AAA
使用字符串"AAA"來替代真正的類,避免泄漏更多的信息