【转】請為你的 Android 程式加上 obfuscation 吧!
Adding ProGuard task into your Android building steps.
在 在 Eclipse 內,用 Ant 編譯你的 Android 程式 這篇中,我介紹過如何利用 Ant 來編譯最佳化的 Android 程式。在那篇中,我只說到加上 debug="false" 和 optimize="true" 這兩個選項。其實,這樣的最佳化還是不夠的。你還要加上 obfuscation 才行。
在 Java 的世界中,有許多的 obfuscators 可以選用。我在這裡,只提這免費的 ProGuard,並和你分享我是如何將 ProGuard 加到 build.xml 之中,以及在 Android projects 中,使用這 obfuscator,應該要注意的事。
ProGuard 可以用來做什麼?
進入 ProGuard 的首頁,第一句話就這麼寫著:
ProGuard is a free Java class file shrinker, optimizer, obfuscator, and preverifier. It detects and removes unused classes, fields, methods, and attributes. It optimizes bytecode and removes unused instructions. It renames the remaining classes, fields, and methods using short meaningless names...
因此,你可以知道 ProGuard 可以幫你,除掉多餘的 classes 或是函式,或是變數,他也會最佳化你的程式。更重要的是,他還會將這些 classes, functions, variables 的名稱,用較短的名稱來取代。這個 obfuscation 功能,除了可以更進一步縮小程式的大小之外,還可以在別人 decompile 你的程式時,延長他讀懂你程式的時間。
用 ProGuard 縮小程式大小的效果,好嗎?就以我手上的一個例子來說,原先的 classes.dex 有 350,864 bytes。經過 ProGuard 處理後,大小變為 226,772 bytes,足足少了有 35.4% 之多。這應該可以減少不少,使用者要下載你程式時,所使用的時間。
將 ProGuard 加到程式的建置步驟中
我的 Eclipse 中,附帶的是 Ant 1.7.0 版本。在這個版本中,已經將 ProGuard 變成是內建的 task 之一,因此你不用再像 這個作法 中一樣,以 java task 執行外部 java 程式的方式,來執行 ProGuard 程式。
你只要像下面這樣,寫個 obfuscate target。
- <target name="obfuscate" depends="compile">
- <jar basedir="${outdir-classes}" destfile="temp.jar"/>
- <taskdef resource="proguard/ant/task.properties"
- classpath="d:/proguard4.2/lib/proguard.jar" />
- <proguard>
- -injars temp.jar
- -outjars obfuscated.jar
- -libraryjars ${android-jar}
- -dontpreverify
- -dontskipnonpubliclibraryclasses
- -dontusemixedcaseclassnames
- -keep public class * extends android.app.Activity
- -keep public class * extends android.content.BroadcastReceiver
- -optimizationpasses 7
- </proguard>
- <delete file="temp.jar"/>
- <delete dir="${outdir-classes-final}" failonerror="false" />
- <mkdir dir="${outdir-classes-final}"/>
- <unzip src="obfuscated.jar" dest="${outdir-classes-final}"/>
- <delete file="obfuscated.jar"/>
- <echo>Obfuscated classes are put to "${outdir-classes-final}".</echo>
- </target>
再將原先 dex target,
- <!-- Convert this project's .class files into .dex files. -->
- <target name="dex" depends="compile">
- ...
- </target>
其中的 depends="compile" 改成 depends="obfuscate"。改好後,就像下面這樣。
- <!-- Convert this project's .class files into .dex files. -->
- <target name="dex" depends="obfuscate">
- ...
- </target>
在使用 ProGuard 的經驗上,我建議是不要加上 -overloadaggressively 這個選項。要不然,有的程式,會在執行時丟出個 java.lang.VerifyError exception。我加上 -dontskipnonpubliclibraryclasses 選項,是因為要避開使用 java.lang.StringBuilder.setLength(int) 時的錯誤。另外,如果你程式的類別數超過 26 個時,我建議要加上 -dontusemixedcaseclassnames 選項,要不然 Android 的 dex compiler 會有問題。
另外,最讓人頭痛的是 -keep 這個選項。到底那些類別要加上這個選項?我的作法是,只要是出現在 AndroidManifest.xml 中的類別,及出現在 layout/*.xml 中的 custom widgets,都要加上 -keep。要不然,Android 系統,可是會找不到你的類別。
關於 ProGuard 的程式碼最佳化部份,我手上幾個程式都沒問題,經過 ProGuard 最佳化後,還是能順利通過 Dalvik 的驗證。不過,倒是有個程式,一直沒法通過 Dalvik 的驗證。最後,還是在稍微改寫了一下程式的寫法,才避免了這個問題。所以,如果有啟用 ProGuard 的程式碼最佳化功能,你可要多注意一下,是否所有的功能,在模擬器上,都可以正常執行。