Android混淆打包那些事兒

ProGuard簡(jiǎn)介

在Android中一提起ProGuard磁携,我們就會(huì)認(rèn)為他是用來(lái)混淆代碼的业舍,殊不知ProGuard一共包括以下4個(gè)功能撤缴。

  • 壓縮(Shrink):偵測(cè)并移除代碼中無(wú)用的類劫扒、字段捐顷、方法荡陷、和特性(Attribute)。
  • 優(yōu)化(OPtimize):對(duì)字節(jié)碼進(jìn)行優(yōu)化迅涮,移除無(wú)用指令亲善。
  • 混淆(Obfuscate):使用a、b逗柴、c蛹头、d這樣簡(jiǎn)短而無(wú)意義的名稱,對(duì)類、字段和方法進(jìn)行重命名渣蜗。
  • 預(yù)檢(Preveirfy): 在java平臺(tái)上對(duì)處理后的代碼進(jìn)行預(yù)檢屠尊。

提示:如果僅僅是為了代碼混淆,ProGuard有一個(gè)兄弟產(chǎn)品DexGuard可以試試耕拷,地址點(diǎn)擊這里讼昆。

ProGuard是一個(gè)開(kāi)源項(xiàng)目在SourceForge上進(jìn)行維護(hù),地址點(diǎn)擊這里骚烧。

從上述地址下載ProGuard之后浸赫,能同時(shí)看到官方文檔和示例,不過(guò)是英文的赃绊,目前市面上沒(méi)有相應(yīng)的中文翻譯版既峡,也沒(méi)有一片詳盡的介紹文章。
  如果你的項(xiàng)目已經(jīng)使用了摸個(gè)版本的ProGuard碧查,比如运敢,現(xiàn)在市面上最流行的是4.7版本,我建議不要進(jìn)行升級(jí)忠售。一切以穩(wěn)定為首传惠,如果一定要升級(jí)到最新版本,請(qǐng)?jiān)谑褂肞roGuard后稻扬,對(duì)項(xiàng)目的所有模塊進(jìn)行全功能的回歸測(cè)試卦方。

ProGuard工作原理

ProGuard由shrink、optimize泰佳、obfuscate和preverify四個(gè)步驟組成盼砍,每個(gè)步驟都是可選的,需要那些步驟都可以在腳本中配置乐纸。流程圖如下:

Input jars衬廷、Library jars-shrink->Shrunk code-optimize->Optim.code-obfuscate->Obfusc.code-preverify->Output >jars摇予、Library jars

這里我們引入Entry Point的概念汽绢。Entry Point實(shí)在ProGuard過(guò)程中不會(huì)被處理的類或方法。再壓縮的步驟中侧戴,ProGuard或從上述的EntryPoint開(kāi)始遞歸遍歷宁昭,搜索那些類和類成員在使用。對(duì)于沒(méi)有被使用的類和類的成員酗宋,就會(huì)在壓縮階段丟棄积仗。
  接下來(lái)優(yōu)化的步驟中,那些非EntryPoint的類蜕猫、方法都會(huì)被設(shè)置為private寂曹、static或final,不使用的參數(shù)會(huì)被移除,此外隆圆,有些方法會(huì)被標(biāo)記為內(nèi)聯(lián)的漱挚。在混淆的步驟中,ProGuard會(huì)對(duì)非EntryPoint的類和方法進(jìn)行重命名渺氧。

如何寫一個(gè)ProGuard文件

下面介紹ProGuard.cfg混淆文件怎么寫旨涝。這是一個(gè)三步走的過(guò)程。

基本混淆

以下是混淆最基本的配置信息侣背,在任何App都要使用白华,可以作為模板使用,我為每行代碼都增加了注釋:

1.基本指令

# 代碼混淆壓縮比贩耐,在0~7之間弧腥,默認(rèn)為5,一般不下需要修改
-optimizationpasses 5

# 混淆時(shí)不使用大小寫混合,混淆后的類名為小寫
# windows下的同學(xué)還是加入這個(gè)選項(xiàng)吧(windows大小寫不敏感)
-dontusemixedcaseclassnames

# 指定不去忽略非公共的庫(kù)的類
# 默認(rèn)跳過(guò)憔杨,有些情況下編寫的代碼與類庫(kù)中的類在同一個(gè)包下鸟赫,并且持有包中內(nèi)容的引用,此時(shí)就需要加入此條聲明
-dontskipnonpubliclibraryclasses

# 指定不去忽略非公共的庫(kù)的類的成員
-dontskipnonpubliclibraryclassmembers

# 不做預(yù)檢驗(yàn)消别,preverify是proguard的四個(gè)步驟之一
# Android不需要preverify抛蚤,去掉這一步可以加快混淆速度
-dontpreverify

# 有了verbose這句話,混淆后就會(huì)生成映射文件
# 包含有類名->混淆后類名的映射關(guān)系
# 然后使用printmapping指定映射文件的名稱
-verbose
-printmapping priguardMapping.txt

# 指定混淆時(shí)采用的算法寻狂,后面的參數(shù)是一個(gè)過(guò)濾器
# 這個(gè)過(guò)濾器是谷歌推薦的算法岁经,一般不改變
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*

# 保護(hù)代碼中的Annotation不被混淆
# 這在JSON實(shí)體映射時(shí)非常重要,比如fastJson
-keepattributes *Annotation*

# 避免混淆泛型
# 這在JSON實(shí)體映射時(shí)非常重要蛇券,比如fastJson
-keepattributes Signature

# 拋出異常時(shí)保留代碼行號(hào)
-keepattributes SourceFile,LineNumberTable

2.需要保留的東西

# 保留所有的本地native方法不被混淆
-keepclasseswithmembernames class * {
    native <methods>;
}

# 保留了繼承自Activity缀壤、Application這些類的子類
# 因?yàn)檫@些子類有可能被外部調(diào)用
# 比如第一行就保證了所有Activity的子類不要被混淆
-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
-keep public class * extends android.view.View
-keep public class com.android.vending.licensing.ILicensingService

# 如果有引用android-support-v4.jar包,可以添加下面這行
-keep public class com.null.test.ui.fragment.** {*;}

# 保留Activity中的方法參數(shù)是view的方法纠亚,
# 從而我們?cè)趌ayout里面編寫onClick就不會(huì)影響
-keepclassmembers class * extends android.app.Activity {
    public void * (android.view.View);
}

# 枚舉類不能被混淆
-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}

# 保留自定義控件(繼承自View)不能被混淆
-keep public class * extends android.view.View {
    public <init>(android.content.Context);
    public <init>(android.content.Context, android.util.AttributeSet);
    public <init>(android.content.Context, android.util.AttributeSet, int);
    public void set*(***);
    *** get* ();
}

# 保留Parcelable序列化的類不能被混淆
-keep class * implements android.os.Parcelable{
    public static final android.os.Parcelable$Creator *;
}

# 保留Serializable 序列化的類不被混淆
-keepclassmembers class * implements java.io.Serializable {
   static final long serialVersionUID;
   private static final java.io.ObjectStreamField[] serialPersistentFields;
   !static !transient <fields>;
   private void writeObject(java.io.ObjectOutputStream);
   private void readObject(java.io.ObjectInputStream);
   java.lang.Object writeReplace();
   java.lang.Object readResolve();
}

# 對(duì)R文件下的所有類及其方法塘慕,都不能被混淆
-keepclassmembers class **.R$* {
    *;
}

# 對(duì)于帶有回調(diào)函數(shù)onXXEvent的蒂胞,不能混淆
-keepclassmembers class * {
    void *(**On*Event);
}
針對(duì)App的量身定制

1.保留實(shí)體類和成員不被混淆
  對(duì)于實(shí)體图呢,要保留它們的get和set方法,對(duì)于boolean型get方法有的命名是isXXX類型骗随,不要遺漏

-keep class com.null.test.entities.** {
    //全部忽略
    *;
}
-keep class com.null.test.entities.** {
    //忽略get和set方法
    public void set*(***);
    public *** get*();
    public *** is*();
}
//以上兩種任意一種都行

一個(gè)項(xiàng)目中最好把所有的實(shí)體都放到同一個(gè)包下蛤织,這樣針對(duì)包混淆就行了,避免了實(shí)體混淆遺漏而造成的崩潰

2.內(nèi)嵌類
  內(nèi)嵌類經(jīng)常容易被混淆鸿染,結(jié)果調(diào)用的時(shí)候?yàn)榭站捅罎⒘酥秆痢W詈玫霓k法就是不用內(nèi)嵌類(有點(diǎn)扯淡),如果MainActivity中使用了涨椒,就用如下代碼

-keep class com.null.test.MainActivity$* {
    *;
}

$這個(gè)符號(hào)就是用來(lái)分割內(nèi)嵌類與其母體的標(biāo)志

3.對(duì)WebView的處理
  如果項(xiàng)目中用到了WebView的復(fù)雜操作摊鸡,請(qǐng)加入以下代碼:

-keepclassmembers class * extends android.webkit.WebViewClient {
    public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap);
    public boolean *(android.webkit.WebView, java.lang.String);
}
-keepclassmembers class * extends android.webkit.WebViewClient {
    public void *(android.webkit.WebView, java.lang.String);
}

4.對(duì)JavaScript的處理

-keepclassmembers class com.null.test.MainActivity$JSInterfacel {
    <methods>;
}
針對(duì)第三方j(luò)ar包的解決方案

一般來(lái)說(shuō)第三方的SDK都是經(jīng)過(guò)ProGuard混淆了的绽媒。我們要做的就是避免其再次在我們的App混淆。
1.針對(duì)andoid-support-v4.jar的解決方案

-libraryjars ./libs/android-support-v4.jar
-dontwarn android.support.v4.** 
-dontwarn **CompatHoneycomb
-dontwarn **CompatHoneycombMR2
-dontwarn **CompatCreatorHoneycombMR2
-keep interface android.support.v4.app.** { *; }
-keep class android.support.v4.** { *; }
-keep public class * extends android.support.v4.**
-keep public class * extends android.app.Fragment

這里注意一個(gè)問(wèn)題就是免猾,很有可能在我們引用的其他包里面也會(huì)依賴v4包些椒,因兩個(gè)(或者以上)v4的版本是不一樣的,在運(yùn)行期間拋出NoClassDefFoundError異常掸刊。相應(yīng)的解決辦法就是都依賴同一個(gè)v4包就行了免糕。

2.其他第三方的jar包的解決方案
  這個(gè)要取決第三方j(luò)ar包的混淆策略了。一般在其官方文檔上面都有混淆說(shuō)明忧侧。比如支付寶相應(yīng)的混淆規(guī)則就是:

-libraryjars ./libs/alipaysdk.jar
-dontwarn com.alipay.android.app.** 
-keep public class com.alipay.** {*;}

其他注意事項(xiàng)

1.確笔ぃ混淆不會(huì)對(duì)項(xiàng)目造成影響

  • 測(cè)試要基于混淆包進(jìn)行
  • 冒煙測(cè)試也要基于混淆包進(jìn)行
  • 發(fā)版本前,要額外測(cè)試正式版的推送蚓炬、分享松逊、打點(diǎn)、二維碼掃描等功能

2.打包時(shí)忽略警告
  當(dāng)在導(dǎo)出時(shí)肯夏,發(fā)現(xiàn)很多could not reference class之類的warning信息经宏,如果確認(rèn)app運(yùn)行中和那些引用沒(méi)有什么關(guān)系的話,就可以添加-dontwarn標(biāo)簽驯击,就不會(huì)在提示這些warning信息了烁兰。如-dontwarn org.apache.**。

3.對(duì)于自定義類庫(kù)的混淆處理
  對(duì)于自定義的類庫(kù)徊都,我們一般是保留類和類的成員沪斟。

4.使用annotation避免混淆

@Keep
@KeepPublicGettersSetters
public class Bean {
    public boolean booleanProperty;
    public int intProperty;
    public String stringProperty;
    public boolean isBooleanProperty() {
        return booleanProperty;
    }
}
//其中@KeepPublicGettersSetters我沒(méi)找到,大家慎用

5.在項(xiàng)目中制定混淆文件
eclipse是在project.properties文件最后加上proguard.config = proguard.cfg
android studio 是在build.gradle修改buildTypes如下:

buildTypes {
    release {
        minifyEnabled true
        shrinkResources true
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末暇矫,一起剝皮案震驚了整個(gè)濱河市主之,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌李根,老刑警劉巖槽奕,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異房轿,居然都是意外死亡粤攒,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門冀续,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)琼讽,“玉大人必峰,你說(shuō)我怎么就攤上這事洪唐。” “怎么了吼蚁?”我有些...
    開(kāi)封第一講書人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵凭需,是天一觀的道長(zhǎng)问欠。 經(jīng)常有香客問(wèn)我,道長(zhǎng)粒蜈,這世上最難降的妖魔是什么顺献? 我笑而不...
    開(kāi)封第一講書人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮枯怖,結(jié)果婚禮上注整,老公的妹妹穿的比我還像新娘。我一直安慰自己度硝,他們只是感情好肿轨,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著蕊程,像睡著了一般椒袍。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上藻茂,一...
    開(kāi)封第一講書人閱讀 51,541評(píng)論 1 305
  • 那天驹暑,我揣著相機(jī)與錄音,去河邊找鬼辨赐。 笑死优俘,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的掀序。 我是一名探鬼主播兼吓,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼森枪!你這毒婦竟也來(lái)了视搏?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤县袱,失蹤者是張志新(化名)和其女友劉穎浑娜,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體式散,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡筋遭,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了暴拄。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片漓滔。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖乖篷,靈堂內(nèi)的尸體忽然破棺而出响驴,到底是詐尸還是另有隱情,我是刑警寧澤撕蔼,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布豁鲤,位于F島的核電站秽誊,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏琳骡。R本人自食惡果不足惜锅论,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望楣号。 院中可真熱鬧最易,春花似錦、人聲如沸炫狱。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)毕荐。三九已至束析,卻和暖如春作谚,著一層夾襖步出監(jiān)牢的瞬間绍申,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工厕妖, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留第美,地道東北人蝶锋。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像什往,于是被迫代替她去往敵國(guó)和親扳缕。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容