受《APP研發錄》啟發,裡面講到一名Android程式員,在工作一段時間後,會感覺到迷茫,想進階的話接下去是看Android系統源碼呢,還是每天繼續做應用,畢竟每天都是畫UI和利用MobileAPI處理Json還是蠻無聊的,做着重複的事情,沒有技術的上提升空間的。是以,根據裡面提到的Android應用開發人員所需要精通的20個技術點,寫篇文章進行總結,一方面是梳理下基礎知識和鞏固知識,另一方面也是彌補自我不足之處。
那麼,今天就來講講ProGuard代碼混淆的相關技術知識點。
ProGuard簡介
ProGuard工作原理
如何編寫一個ProGuard檔案
其他注意事項
小結
因為Java代碼是非常容易反編碼的,況且Android開發的應用程式是用Java代碼寫的,為了很好的保護Java源代碼,我們需要對編譯好後的class檔案進行混淆。
ProGuard是一個混淆代碼的開源項目,它的主要作用是混淆代碼,殊不知ProGuard還包括以下4個功能。
壓縮(Shrink):檢測并移除代碼中無用的類、字段、方法和特性(Attribute)。
優化(Optimize):對位元組碼進行優化,移除無用的指令。
混淆(Obfuscate):使用a,b,c,d這樣簡短而無意義的名稱,對類、字段和方法進行重命名。
預檢(Preveirfy):在Java平台上對處理後的代碼進行預檢,確定加載的class檔案是可執行的。
總而言之,根據官網的翻譯:Proguard是一個Java類檔案壓縮器、優化器、混淆器、預校驗器。壓縮環節會檢測以及移除沒有用到的類、字段、方法以及屬性。優化環節會分析以及優化方法的位元組碼。混淆環節會用無意義的短變量去重命名類、變量、方法。這些步驟讓代碼更精簡,更高效,也更難被逆向(破解)。
ProGuar由shrink、optimize、obfuscate和preveirfy四個步驟組成,每個步驟都是可選的,我們可以通過配置腳本來決定執行其中的哪幾個步驟。

混淆就是移除沒有用到的代碼,然後對代碼裡面的類、變量、方法重命名為人可讀性很差的簡短名字。
那麼有一個問題,ProGuard怎麼知道這個代碼沒有被用到呢?
這裡引入一個Entry Point(入口點)概念,Entry Point是在ProGuard過程中不會被處理的類或方法。在壓縮的步驟中,ProGuard會從上述的Entry Point開始遞歸周遊,搜尋哪些類和類的成員在使用,對于沒有被使用的類和類的成員,就會在壓縮段丢棄,在接下來的優化過程中,那些非Entry Point的類、方法都會被設定為private、static或final,不使用的參數會被移除,此外,有些方法會被标記為内聯的,在混淆的步驟中,ProGuard會對非Entry Point的類和方法進行重命名。
那麼這個入口點怎麼來呢?就是從ProGuard的配置檔案來,隻要這個配置了,那麼就不會被移除。
有個三步走的過程:
基本混淆
針對APP的量身定制
針對第三方jar包的解決方案
混淆檔案的基本配置資訊,任何APP都要使用,可以作為模闆使用,具體如下。
1,基本指令
2,需要保留的東西
1,保留實體類和成員被混淆
對于實體,保留它們的set和get方法,對于boolean型get方法,有人喜歡命名isXXX的方式,是以不要遺漏。如下:
一種好的做法是把所有實體都放在一個包下進行管理,這樣隻寫一次混淆就夠了,避免以後在别的包中新增的實體而忘記保留,代碼在混淆後因為找不到相應的實體類而崩潰。
2,内嵌類
内嵌類經常會被混淆,結果在調用的時候為空就崩潰了,最好的解決方法就是把這個内嵌類拿出來,單獨成為一個類。如果一定要内置,那麼這個類就必須在混淆的時候保留,比如如下:
這個$符号就是用來分割内嵌類與其母體的标志。
3,對WebView的處理
4,對JavaScript的處理
其中JSInterface是MainActivity的子類
5,處理反射
在程式中使用SomeClass.class.method這樣的靜态方法,在ProGuard中是在壓縮過程中被保留的,那麼對于Class.forName("SomeClass")呢,SomeClass不會被壓縮過程中移除,它會檢查程式中使用的Class.forName方法,對參數SomeClass法外開恩,不會被移除。但是在混淆過程中,無論是Class.forName("SomeClass"),還是SomeClass.class,都不能蒙混過關,SomeClass這個類名稱會被混淆,是以,我們要在ProGuard.cfg檔案中保留這個類名稱。
<code>Class.forName("SomeClass")</code>
<code>SomeClass.class</code>
<code>SomeClass.class.getField("someField")</code>
<code>SomeClass.class.getDeclaredField("someField")</code>
<code>SomeClass.class.getMethod("someMethod", new Class[] {})</code>
<code>SomeClass.class.getMethod("someMethod", new Class[] { A.class })</code>
<code>SomeClass.class.getMethod("someMethod", new Class[] { A.class, B.class })</code>
<code>SomeClass.class.getDeclaredMethod("someMethod", new Class[] {})</code>
<code>SomeClass.class.getDeclaredMethod("someMethod", new Class[] { A.class })</code>
<code>SomeClass.class.getDeclaredMethod("someMethod", new Class[] { A.class, B.class })</code>
<code>AtomicIntegerFieldUpdater.newUpdater(SomeClass.class, "someField")</code>
<code>AtomicLongFieldUpdater.newUpdater(SomeClass.class, "someField")</code>
<code>AtomicReferenceFieldUpdater.newUpdater(SomeClass.class, SomeType.class, "someField")</code>
在混淆的時候,要在項目中搜尋一下上述方法,将相應的類或者方法的名稱進行保留而不被混淆。
6,對于自定義View的解決方案
但凡在Layout目錄下的XML布局檔案配置的自定義View,都不能進行混淆。為此要周遊Layout下的所有的XML布局檔案,找到那些自定義View,然後确認其是否在ProGuard檔案中保留。有一種思路是,在我們使用自定義View時,前面都必須加上我們的包名,比如com.a.b.customeview,我們可以周遊所有Layout下的XML布局檔案,查找所有比對com.a.b的标簽即可。
我們在Android項目中不可避免要使用很多第三方提供的SDK,一般而言,這些SDK是經過ProGuard混淆的,而我們所需要做的就是避免這些SDK的類和方法在我們APP被混淆。
1,針對android-support-v4.jar的解決方案
2,其他的第三方jar包的解決方案
這個就取決于第三方包的混淆政策了,一般都有在各自的SDK中有關于混淆的說明文字,比如支付寶如下:
值得注意的是,不是每個第三方SDK都需要-dontwarn 指令,這取決于混淆時第三方SDK是否出現警告,需要的時候再加上。
當然在使用ProGuard過程中,還有一些注意的事項,如下。
1,如何確定混淆不會對項目産生影響
測試工作要基于混淆包進行,才能盡早發現問題
每天開發團隊的冒煙測試,也要基于混淆包
發版前,重點的功能和子產品要額外的測試,包括推送,分享,打賞
2,打包時忽略警告
當導出包的時候,發現很多could not reference class之類的warning資訊,如果确認App在運作中和那些引用沒有什麼關系,可以添加-dontwarn 标簽,就不會提示這些警告資訊了
3,對于自定義類庫的混淆處理
比如我們引用了一個叫做AndroidLib的類庫,我們需要對Lib也進行混淆,然後在主項目的混淆檔案中保留AndroidLib中的類和類的成員。
4,使用annotation避免混淆
另一種類或者屬性被混淆的方式是,使用annotation,比如這樣:
5,在項目中指定混淆檔案
到最後,發現沒有介紹如何在項目中指定混淆檔案。在項目中有一個project.properties檔案,在其中寫這麼一句話,就可以確定每次手動打包生成的apk是混淆過的。
proguard.config=proguard.cfg
其中,proguard.cfg是混淆檔案的名稱。
總之ProGuard是一個比較枯燥的過程,但Android項目沒有了ProGuard就真不行了,這樣可以保證我們開發出的APK可以更健壯,畢竟很多核心代碼品質也算是一個APK的核心競争力吧。
源于對掌握的Android開發基礎點進行整理,羅列下已經總結的文章,從中可以看到技術積累的過程。
1,Android系統簡介
2,ProGuard代碼混淆
3,講講Handler+Looper+MessageQueue關系
4,Android圖檔加載庫了解
5,談談Android運作時權限了解
6,EventBus初了解
7,Android 常見工具類
8,對于Fragment的一些了解
9,Android 四大元件之 " Activity "
10,Android 四大元件之" Service "
11,Android 四大元件之“ BroadcastReceiver "
11,Android 四大元件之" ContentProvider "
13,講講 Android 事件攔截機制
14,Android 動畫的了解
15,Android 生命周期和啟動模式
16,Android IPC 機制
17,View 的事件體系
18,View 的工作原理
19,了解 Window 和 WindowManager
20,Activity 啟動過程分析
21,Service 啟動過程分析
22,Android 性能優化
23,Android 消息機制
24,Android Bitmap相關
25,Android 線程和線程池
26,Android 中的 Drawable 和動畫
27,RecylerView 中的裝飾者模式
28,Android 觸摸事件機制
29,Android 事件機制應用
30,Cordova 架構的一些了解
31,有關 Android 插件化思考
32,開發人員必備技能——單元測試