標籤:
由於工作需要,這兩天和同事在研究android下面的ProGuard工具的使用,通過查看android官網對該工具的介紹以及網路上其它相關資料,再加上自己的親手實踐,算是有了一個基本瞭解。下面將自己的理解和認識簡要的做個筆記,有異議或者不解的,可以直接留言。
什麼是ProGuard工具?
ProGuard是android提供的一個免費的工具,它能夠移除工程中一些沒用的代碼,或者使用語義上隱晦的名稱來重新命名代碼中的類、欄位和函數等,達到壓縮、最佳化和混淆代碼的功能。具體來說,使用ProGuard工具,可以達到下面兩個目的:
- 刪除了源檔案中沒有調用的那部分代碼,最大化的精簡了位元組碼檔案,使得最終產生的apk檔案更小。
- 使用語義混淆的命名替換了代碼中的類、欄位和函數等,使得其他人無法反編譯擷取原始碼,起到對代碼的保護作用。
我看網上有不少人根據ProGuard工具的作用,直接稱呼其為“混淆代碼工具”,本文也暫時用這個詞簡稱。
更多的理解,可以參考ProGuard工具的官方文檔地址:http://developer.android.com/tools/help/proguard.html
ProGuard工具的整合與使用環境
其實,ProGuard工具是已經整合到我們android系統中的,所以不需要使用者手動的去整合。但是有一點需要注意,僅在程式處於Release模式時ProGuard才有效,反之在Debug模式是不能通過ProGuard來混淆代碼的。
根據ProGuard的具體使用環境,我分在Eclipse工具和android源碼兩種編譯環境淺談ProGuard的使用方法。
Eclipse環境中ProGuard的使用
以我電腦的android4.0環境為例,當我們在Eclipse中建立一個項目,或者匯入一個已存在項目(保證當前項目沒有語法錯誤)後,在工程的根目錄,會自動產生兩個ProGuard的混淆檔案:proguard-project.txt和project.properties(在老版本的ADT中,只會產生一個叫proguard.cfg的檔案)。我們先看下檔案project.properties :
# This file is automatically generated by Android Tools.# Do not modify this file -- YOUR CHANGES WILL BE ERASED!## This file must be checked in Version Control Systems.## To customize properties used by the Ant build system edit# "ant.properties", and override values to adapt the script to your# project structure.## To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt# Project target.target=android-16
看後面一段注釋:To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home) ,意指要讓ProGuard 來壓縮和混淆代碼,把這句注釋去掉即可!所以,我們只要把下面一句注釋取消即可,如下所示:
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
我們仔細的看下這部分代碼:這個地方是通過設定proguard.config屬性來指定混淆代碼的設定檔為本機SDK目錄下面的proguard-android.txt檔案,以及制定混淆的個人化設定檔為當前工程(eclipse下)根目錄下面的proguard-project.txt檔案 ,而後面這個檔案,恰是我們剛才看到的原本在根目錄下自動產生的另外一個檔案!
其實開啟了這個地方,我們就已經可以混淆代碼了,不過這裡要注意:不能試圖通過運行eclipse中的Run as 和 Debug as 菜單來產生混淆代碼,必須通過如所示的方法將apk匯出才行,當然你可以選擇“簽名”或者“不簽名”:
這樣一步操作後,算是代碼混淆完成了。那麼怎麼才能檢驗我們真的混淆了代碼了呢?首先,我們能夠看到在工程的根目錄新生產了一個檔案夾proguard,裡面有四個檔案,其內容如下:
- dump.txt : 描述了apk中所有類 檔案中內部的結構體。( Describes the internal structure of all the class files in the .apk file )
- mapping.txt : 列出了原始的類、方法和名稱與混淆代碼見得映射。( Lists the mapping between the original and obfuscated class, method, and field names. )
- seeds.txt : 列出了沒有混淆的類和方法。( Lists the classes and members that are not obfuscated )
- usage.txt : 列出congapk中刪除的代碼。( Lists the code that was stripped from the .apk )
同時使用反編譯軟體對新產生的apk反編譯後會發現,裡面的類名、方法和變數等,都變成了簡單的a、b、c、d等毫無含義的字母,這樣就達到了混淆的目的:
但在實際使用過程中,我們會發現當前apk中的有些方法和類,是要供外部使用的,而此時混淆了名稱,外部調用就會報錯了,那麼怎麼解決這個問題?此時就要用到我們剛才提到的混淆的個人化設定檔proguard-project.txt,在其中去配置不需要混淆的類、方法和變數等。關於混淆檔案的具體配置方法,請看下面的最後一個標題會有詳述。
Android源碼環境中ProGuard使用
在Google發布的android源碼中,面對那麼多代碼和檔案目錄,此時該如何混淆代碼與配置混淆檔案呢?
android中預設是將代碼混淆ProGuard關閉的,在alps/build/core/proguard.flags中有下面一句,意指將預設不混淆,不需要代碼刪除,我們將這一句注釋起來,就起到代碼混淆編譯的作用。
# Don‘t obfuscate. We only need dead code striping.-dontobfuscate
可以說,這句是android工程中代碼混淆的總開關,然而,注釋了上面的代碼後,整個工程就已經是代碼混淆了嗎?不是的,這裡還要關注一個檔案alps/build/core/package.mk,在這個檔案中有這麼一段:
ifndef LOCAL_PROGUARD_ENABLEDifneq ($(filter user userdebug, $(TARGET_BUILD_VARIANT)),) # turn on Proguard by default for user & userdebug build #LOCAL_PROGUARD_ENABLED :=fullendifendif
切記:當我們需要對整個工程進行代碼混淆的時候,就把此處的 #LOCAL_PROGUARD_ENABLED :=full注釋去掉,成為有效宏即可。如果不想對整個工程代碼混淆,而只是相對某個模組混淆的話,就先不要動這裡的代碼。
接著建議將真箇工程new一遍,之後就可以針對具體的apk檔案進行混淆檔案的設定和定製了。下面以alps/packages/apps/Music為例說說該如何對特定模組做到混淆代碼:
在Music目錄下,我們看到一個平時不太關注,但今天一定很在意的檔案名稱:proguard.flags ,對了,這個檔案就是Music的混淆設定檔,可以開啟看看(有些地方沒有這個檔案,使用者可以自己手動建立一下,最好名稱也叫proguard.flags,android下預設都是這個名字)。當然,設定了設定檔還是不夠的,還需要在同目錄的Android.mk中如下設定如下兩句:
LOCAL_PROGUARD_ENABLED := fullLOCAL_PROGUARD_FLAG_FILES := proguard.flags
只有這樣才能讓混淆代碼有效,並且將混淆設定檔關聯起來。(有些模組沒有這兩句句,就自己手動加上)
反之,如果使用者已經在alps/build/core/package.mk開啟了全工程混淆編譯的控制點後,又在針對某個模組時不想混淆編譯怎麼辦?這就簡單了,將模組下的Android.mk中設定為**LOCAL_PROGUARD_ENABLED := disabled**即可。
這樣,我們通過mm編譯後的代碼產生的apk,或者new真箇工程後產生的燒機代碼,都是已經添加相應配置的混淆代碼了。
反編譯後,除過proguard.flags中定製的不需要混淆的代碼外,其他都是被混淆了,是android中Music模組的混淆後反編譯結果:
混淆檔案的配置
在實際使用過程中,我們會發現當前apk中的有些方法和類,是要供外部使用的,而此時混淆了名稱,外部調用就會報錯了,那麼怎麼解決這個問題?此時就需要我們配置混淆的個人化檔案proguard-project.txt(eclipse環境中)或者proguard.flags(android源碼環境),在其中去配置不需要混淆的類、方法和變數等。關於混淆檔案的具體配置方法,大家可以去搜尋下,我這裡提供一段網上有人共用的配置代碼,這個配置代碼保留了工程檔案中的Activity、Application、Service、BroadcastReceiver、ContentProvider、BackupAgentHelper、Preference和ILicensingService的子類,並保留了所有的Native變數名及類名,所有類中部分以設定了固定參數格式的建構函式,枚舉等等,用來防止外部調用出錯,大家可以借鑒下,以後來配置自己的檔案:
-optimizationpasses 5-dontusemixedcaseclassnames-dontskipnonpubliclibraryclasses-dontpreverify-verbose-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*-keep public class * extends android.app.Activity-keep public class * extends android.app.Application-keep public class * extends android.app.Service-keep public class * extends android.content.BroadcastReceiver-keep public class * extends android.content.ContentProvider-keep public class * extends android.app.backup.BackupAgentHelper-keep public class * extends android.preference.Preference -keepclasseswithmembernames class * { native <methods>;}-keepclasseswithmembers class * { public <init>(android.content.Context, android.util.AttributeSet);}-keepclasseswithmembers class * { public <init>(android.content.Context, android.util.AttributeSet, int);}-keepclassmembers class * extends android.app.Activity { public void *(android.view.View);}-keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String);}-keep class * implements android.os.Parcelable { public static final android.os.Parcelable$Creator *;}
Eclipse與Android源碼中ProGuard工具的使用