ProGuard 配置使用筆記

1.基本指令java

代碼混淆壓縮比,在0~7之間,默認爲5,通常不須要改

-optimizationpasses 5android

混淆時不使用大小寫混合,混淆後的類名爲小寫

-dontusemixedcaseclassnamesweb

指定不去忽略非公共的庫的類

-dontskipnonpubliclibraryclasses算法

指定不去忽略非公共的庫的類的成員

-dontskipnonpubliclibraryclassmembersapache

不作預校驗, preverify是proguard的4個步驟之一

Android不須要preverify,去掉這一步可加快混淆速度

-dontpreverify安全

有了verbose這句話,混淆後就會生成映射文件

包含有類名->混淆後類名的映射關係

而後使用printmapping指定映射文件的名稱

-verbose
-printmapping proguardMapping.txtmarkdown

指定混淆時採用的算法,後面的參數是一個過濾器

這個過濾器是谷歌推薦的算法,通常不改變

-optimizations !code/simplification/arithmetic,!field/,!class/merging/app

保護代碼中的Annotation不被混淆

這在JSON實體映射時很是重要,好比 fastJson

-keepattributes Annotationeclipse

避免混淆泛型,

這在JSON實體映射時很是重要,好比 fastJson

-keepattributes Signatureide

//拋出異常時保留代碼行號,在第6章異常分析中咱們提到過
-keepattributes SourceFile,LineNumberTable
-dontskipnonpubliclibraryclasses用於告訴ProGuard,不要跳過對非公開類的處理。默認狀況下是跳過的,由於程序中不會引用它們,有些狀況下人們編寫的代碼與類庫中的類在同一個包下,而且對包中內容加以引用,此時須要加入此條聲明。

對於-dontusemixedcaseclassnames,Microsoft Windows用戶請注意:默認狀況下,ProGuard假定你使用的操做系統可以區分兩個只是大小寫不一樣的文件名(好比,A.java和a.java被認爲是兩個不一樣的文件)。顯然Microsoft Windows不是這樣的操做系統(Windows是對文件名是大小寫不敏感的)。所以Windows用戶必須爲ProGurad指定-dontusemixedcaseclassnames選項。若是不這麼作而且你的項目中有超過26個類的話,那麼ProGuard就會默認混用大小寫文件名,而致使class文件相互覆蓋。安全起見,從0.9.0版本開始,EclipseME默認爲ProGuard設置-dontusemixedcaseclassnames選項。項目中有不少類的UNIX用戶能夠刪除這個選項,這樣最終產生的JAR文件的大小能夠進一步縮小。

2.須要保留的東西

保留全部的本地native方法不被混淆

-keepclasseswithmembernames class * {
native ;
}

保留了繼承自Activity、Application這些類的子類

由於這些子類都有可能被外部調用

好比說,第一行就保證了全部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.tuniu.app.ui.fragment.* {;}

保留在Activity中的方法參數是view的方法,

從而咱們在layout裏面編寫onClick就不會被影響

-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 {
* get*();
void set*(*);
public (android.content.Context);
public (android.content.Context, android.util.AttributeSet);
public (android.content.Context, android.util.AttributeSet, int);
}

保留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;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}

對於R(資源)下的全部類及其方法,都不能被混淆

-keep class *.R$ {
*;
}

對於帶有回調函數onXXEvent的,不能被混淆

-keepclassmembers class * {
void *(**On*Event);
}

3.針對App的量身定製

1.保留實體類和成員不被混淆
對於實體,要保留它們的set和get方法,對於boolean型get方法,有人喜歡命名爲isXXX的方式,因此不要遺漏了。
-keep public class com.youndheart.entity.** {
public void set*(*);
public * get*();
public * is*();
}
一種好的作法是把全部實體都放在一個包下進行管理,這樣只寫一次混淆就夠了。避免之後在別的包中新增的實體而忘記保留,代碼在混淆後由於找不到相應的實體類而崩潰。

2.內嵌類
內嵌類常常會被混淆,結果在調用的時候爲空就崩潰了。最好的解決辦法就是把這個內嵌類拿出來,單獨成爲一個類。
若是必定要內置,那麼這個類就必須在混淆時進行保留。好比說com.example.youngheart包下面的MainActivity,它有一些內嵌類,如下指令保留MainActivity的全部內嵌類:
-keep class com.example.youngheart.MainActivity ; 這個符號就是用來分割內嵌類與其母體的標誌。還記得4.1.2中保留R(資源)下面的全部類及其方法的指令嗎?一模一樣:
-keep class *.R$ {*;}

3.對WebView的處理
若是項目中用到了WebView的複雜操做,請加入如下這兩段代碼:
-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.對JavaScript的處理[註釋]
App應用要常常與HTML5頁面的JavaScript進行交互,以下所示:
class JSInteface1 {
@JavascriptInterface
public void callAndroidMethod(int a, fioat b, String c, boolean d) {
if (d) {
String strMessage = 「-」 + (a + 1) + 「-」 + (b + 1) + 「-」 + c
+ 「-」 + d;
new AlertDialog.Builder(MainActivity.this).setTitle(「title」)
.setMessage(strMessage).show();
}
}
}
這個例子參見第3章中3.4節介紹的App與HTML5之間的交互。我接下來要討論的是,如何確保這些js要調用的原生方法不被混淆。
JSInterface是MainActivity的子類,因此保留指令要這麼寫:
-keepclassmembers
class com.example.youngheart.MainActivity$JSInterface1 {
;
}
請在項目中搜索addJavascriptInterface,咱們要對全部使用的地方設置保留指令。

5.處理反射
也許有人會問,在程序中使用SomeClass.class.method1這樣的靜態方法,ProGuard如何處理?
答案是,被引用的類,如SomeClass,確定會在壓縮過程當中被保留。
那麼對於Class.forName(「SomeClass」)呢?SomeClass不會在壓縮過程當中被移除,ProGuard在這點上仍是蠻聰明的,它會檢查程序中使用的Class.forName方法,對參數SomeClass這樣的字符串則法外開恩,不會移除。
可是,在混淆過程當中,不管是Class.forName(「SomeClass」),仍是SomeClass.class,就都不能矇混過關了。SomeClass這個類的名稱會被混淆。所以,咱們要在ProGuard.cfg文件中,保留這個類名稱。
不光是Class.forName(「SomeClass」),如下方法也一樣適用:
·SomeClass.class.getField(「someField」)
·SomeClass.class.getDeclaredField(「someField」)

6.對於自定義View的解決方案
但凡是在layout目錄下的xml佈局文件中配置的自定義View,都不能進行混淆。爲此要遍歷layout下全部的xml佈局文件,找到那些自定義View,而後確認其是否在proguard文件中保留了。
另外一種查找思路是,在使用咱們的自定義View時,前面都必須加上咱們本身的包名,例如com.a.b.customeview,咱們能夠遍歷全部layout下的xml佈局文件,查找全部匹配com.a.b的標籤便可。

4 針對第三方jar包的解決方案

咱們在Android項目中不可避免地要使用到不少第三方提供的SDK。通常而言,這些SDK都是通過ProGuard混淆了的。而咱們所要作的,是避免這些SDK的類和方法在咱們的App中被混淆。

1.針對android-support-v4.jar的解決方案
-libraryjars libs/android-support-v4.jar
-dontwarn android.support.v4.**
-keep class android.support.v4.* { ; }
-keep interface android.support.v4.app.* { ; }
-keep public class * extends android.support.v4.**
-keep public class * extends android.app.Fragment
這裏介紹一下android-support-v4.jar[註釋]。因爲咱們一直使用eclipse之類的IDE進行Android開發,IDE會自動幫咱們把android-support-v4.jar這個jar添加到lib目錄下並進行引用,以至不少開發人員搞不清這個jar究竟是用來幹嗎的。
其實這個jar包是google提供的,全稱是Android Support Library package,它有v四、v7和v13一共3個版本v4這個包是爲了照顧Android 1.6及更高版本而設計的,這個包是使用最普遍的,eclipse新建工程時,都默認帶有這個包。而v7和v13這兩個版本向下兼容的版本很高,因此用的人很少。
android-support-v4.jar這個jar包有不一樣的版本,因此咱們在使用一些第三方jar包時,由於它們也用到了android-support-v4.jar,可是版本不同,就會在運行期拋出NoClassDef-FoundError異常:
相應的解決辦法就是將兩個android-support-v4.jar都用一個就好了。

2.其餘的第三方jar包的解決方案
這個就要取決於第三方jar包的混淆策略了。它們會在各自的SDK中有關於混淆的說明文字。好比支付寶,相應的混淆規則是:
-libraryjars libs/alipaysdk.jar
-dontwarn com.alipay.android.app.**
-keep public class com.alipay.* { ; }
不勝枚舉,爲了不有SDK遺漏沒有進行混淆處理,一個好的作法是,打開libs目錄,看看有多少個jar包,每一個都進行相似的處理。
值得注意的是,不是每一個第三方SDK都須要-dontwarn指令,這取決於混淆時第三方SDK是否會出現警告。須要的時候再加上。

5 其餘注意事項

接下來介紹一些使用ProGuard過程當中須要注意的事項。

1.如何確保混淆不會對項目產生影響
若是一個Android項目從一開始就進行了混淆工做,那麼:
·測試工做要基於混淆包進行,才能儘早發現問題。
·天天開發團隊的冒煙測試,也要基於混淆包進行。
·發版前,要額外測試正式版的推送、分享、打點、二維碼掃描等功能。

2.打包時忽略警告
當在導出時,發現不少could not reference class之類的warning信息,若是確認App在運行中和那些引用沒有什麼關係的話,能夠添加-dontwarn標籤,就不會再提示這些warning信息了。如:-dontwarn org.apache.**。
不要使用-ignorewarnings語句,它會忽略全部警告,這會有很大的潛在風險。

3.對於自定義類庫的混淆處理
若是咱們編寫了一個AndroidLib類庫,咱們的App應用要引用這個類庫。咱們努力在作的是,把業務無關的邏輯抽離到AndroidLib類庫中,而在App應用中只關心業務邏輯。
咱們須要對Lib也進行混淆,而後在主項目的混淆文件中保留AndroidLib中的類和類的成員。

4.使用annotation避免混淆
另外一種避免類或者屬性被混淆的方式是,使用annotation。在須要保留的類中加上以下語法:
@Keep
@KeepPublicGettersSetters
public class Bean {
public boolean booleanProperty;
public int intProperty;
public String stringProperty;
public boolean isBooleanProperty() {
return booleanProperty;
}
}
這種使用方式多出如今fastJSON的使用上。

6.在項目中指定混淆文件

說到最後,發現沒有介紹如何在項目中指定混淆文件。 在項目中有一個project.properties文件,在其中寫這麼一句話,就能夠確保每次手動打包生成的apk是混淆過的: proguard.config = proguard.cfg 其中,proguard.cfg是混淆文件的名稱。

相關文章
相關標籤/搜索