Android Proguard混淆打包經驗總結
作為一名 Android 開發,應該了解并嘗試給自己的項目進行Proguard混淆打包。項目經過Proguard混淆打包后,會發現apk包體積會變小,也就是混淆可以使得apk瘦身;并且反編譯apk的時候會發現, 項目中的源碼都被處理過,進一步保障了apk的安全;這就是我所理解的Proguard混淆打包的兩個優點。不過,想要真正給自己的項目進行Proguard混淆打包,可不是一件容易的事情,真正嘗試去做了,才會發現有好多問題需要去解決,畢竟,混淆打包是針對特定的項目,每個項目需要混淆的代碼都是有區別的。不過,所有apk混淆打包,也有一些通用的規則處理,像這些規則,就可以自己記錄下來,這樣其他項目混淆打包的時候就可以復制粘貼使用混淆代碼了。好了,進入正題,如何給自己的項目量身定制一套Proguard混淆代碼了?
關于Proguard混淆,給APP瘦身,Google官方也給出了文檔給了大致解釋,大家有興趣可以看看,純英文(夠嗆),不過怎么在Android Studio配置Proguard混淆,還是可以看懂的:
我們可以從這些方面對apk瘦身:
1)冗余的代碼,比如多余的jar包代碼;
2)未使用的靜態代碼;
3)資源代碼的冗余;
4)native code
5)圖片資源的優化和壓縮
ProGuard混淆打包,解決的問題主要針對第一點,一般項目進行ProGuard混淆打包后,apk的體積會減小200-500KB左右。
一、在Android Studio中配置Proguard混淆
buildTypes {
release {
//混淆
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
配置很簡單,只需要把minifyEnabled的狀態改成true即可,然后,當進行apk打包時,編譯工具首先會讀取proguar-rules.pro文件。所有混淆規則,即項目的專屬混淆配置代碼就寫在該文件里面。
二、編寫ProGuard文件
proguar-rules.pro文件目錄位置
1、ProGuard混淆配置文件通用配置
這里形容的通用配置,也就是需要添加一些不需要混淆的代碼,比如Android的四大組件的相關代碼,自定義View相關代碼,項目中引用的第三方jar包相關代碼等等;
1)ProGuadr混淆代碼屬性配置代碼
-optimizationpasses 5
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontpreverify
-verbose
-dontwarn
-dontskipnonpubliclibraryclassmembers
-ignorewarnings
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
-keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,*Annotation*,EnclosingMethod
# 保持 native 方法不被混淆
-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);
}
# 泛型與反射
-keepattributes Signature
-keepattributes EnclosingMethod
-keepattributes *Annotation*
以上屬性基本配置代碼中,配置了ProGuard混淆代碼壓縮比例等基本屬性,配置了本地native方法、泛型與反射、注解、內部類、異常處理等相關代碼的混淆配置,保證這些相關的代碼不被混淆,基本上所有項目都可以通用。
1)四大組件ProGuard配置代碼
-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 com.android.vending.licensing.ILicensingService
-keep public class * extends android.os.IInterface
2)項目中的activity、constants、model、bean等混淆配置
比如我的項目結構如下
針對我的項目配置相關配置代碼如下:
-keep class com.example.proguard.model.** {*;}
-keep class com.example.proguard.activity.** {*;}
-keep class com.example.proguard.adapter.** {*;}
-keep class com.example.proguard.constants.**{*;}
-keep class com.example.proguard.base.**{*;}
-keep class com.example.proguard.model.**{*;}
-keep class com.example.proguard.service.**{*;}
-keep class com.example.proguard.weidgt.**{*;}
-keep class com.example.proguard.utils.** {*;}
-keep class com.example.proguard.network.api** {*;}
3)第三方jar包混淆配置代碼
比如我的項目中涉及到的第三方jar包如下:
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.0.0'
compile 'com.android.support:design:23.1.1'
compile 'com.squareup.okhttp3:okhttp:3.2.0'
compile 'com.squareup.retrofit2:retrofit:2.0.0'
compile 'com.squareup.retrofit2:converter-gson:2.0.0'
compile 'com.squareup.retrofit2:adapter-rxjava:2.0.0'
compile 'com.jakewharton:butterknife:7.0.1'
compile 'io.reactivex:rxjava:1.1.0'
compile 'io.reactivex:rxandroid:1.1.0'
compile 'io.reactivex:rxjava:1.0.10'
compile 'com.jakewharton.rxbinding:rxbinding:0.4.0'
compile 'com.jakewharton.rxbinding:rxbinding-appcompat-v7:0.4.0'
compile 'com.jakewharton.rxbinding:rxbinding-design:0.4.0'
compile 'com.非死book.fresco:fresco:0.8.1+'
compile 'com.非死book.fresco:drawee:0.5.0+'
compile 'com.非死book.fresco:fbcore:0.5.0+'
compile 'com.非死book.fresco:imagepipeline:0.5.0+'
compile 'com.google.code.gson:gson:2.7'
compile 'com.orhanobut:logger:1.8'
compile 'com.非死book.stetho:stetho:1.3.1'
compile 'de.greenrobot:greendao:1.3.7'
// 時間日期選擇控件 里面包含nineold動畫jar包
compile 'com.github.flavienlaurent.datetimepicker:library:0.0.2'
//Android 6.0f權限檢查
compile 'gun0912.ted:tedpermission:1.0.0'
我需要的ProGuard配置代碼如下:
#保持第3方jar包不混淆
-keep class com.alibaba.sdk.android.** {*;}
-keep class com.squareup.okhttp.** {*;}
-keep class com.squareup.okhttp3.** {*;}
-keep class com.squareup.retrofit2.** {*;}
-keep class com.jakewharton.** {*;}
-keep class io.reactivex.** {*;}
-keep class rx.** {*;}
-keep class com.jakewharton.rxbinding.** {*;}
-keep class com.非死book.** {*;}
-keep class com.butterknife.** {*;}
-keep class com.google.code.gson.** {*;}
-keep class com.orhanobut.** {*;}
-dontwarn de.greenrobot.daogenerator.**
-keep class de.greenrobot.** {*;}
-keep class com.github.flavienlaurent.datetimepicker.** {*;}
-keep class gun0912.ted.** {*;}
4)針對support4包的配置
-dontwarn android.support.**
-dontwarn android.support.v4.**
-keep class android.support.v4.** { *; }
-keep public class * extends android.support.v4.**
-keep public class * extends android.support.v4.app.Fragment
針對supportv7包的配置一樣
5)針對butterknife的配置
#####butterknife#######
-keep class butterknife.** { *; }
-dontwarn butterknife.internal.**
-keep class **$$ViewBinder { *; }
-dontwarn butterknife.**
-keepclasseswithmembernames class * {
@butterknife.* <fields>;
}
-keepclasseswithmembernames class * {
@butterknife.* <methods>;
}
6)針對gson的配置
-keep class com.google.gson.** {*;}
#-keep class com.google.**{*;}
-keep class sun.misc.Unsafe { *; }
-keep class com.google.gson.stream.** { *; }
-keep class com.google.gson.examples.android.model.** { *; }
-keep class com.google.** {
;
;
}
-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();
}
-dontwarn com.google.gson.**
7)針對greenDao數據庫的配置
-keep class de.greenrobot.dao.** {*;}
#保持greenDao的方法不被混淆 用來保持生成的表名不被混淆
-keepclassmembers class * extends de.greenrobot.dao.AbstractDao {
public static java.lang.String TABLENAME;
}
-keep class **$Properties
8)針對okhttp的配置
# OkHttp
-dontwarn com.squareup.okhttp.**
-keep class com.squareup.okhttp.** {*;}
-keep interface com.squareup.okhttp.** {*;}
-dontwarn okio.**
等等,針對某個jar包的具體混淆配置,大家可以去Google,這里我就列舉這些了。
至此,針對你項目的專屬ProGuard配置文件就出來了,打個正式包看看apk包有沒有瘦身吧。
三、ProGuard混淆打包過程的問題
當你配置好項目的ProGuard文件后,打包過程可能就報錯了,這些錯誤需要我們一一去解決,這里列出幾個我在打包過程遇到的幾個錯誤及解決方法:
1) java.lang.UnsupportedOperationException: Unknown ASTNode child: LambdaExpression
2)如果配置好ProGuard文件后,并且打包也順利進行,打包成功了,但是當你運行瘦身后的apk,會發現apk會出現崩潰的情況,如果出現這種情況,說明配置的ProGuard文件還有問題,有些地方的代碼混淆配置還需要更改,這時候就需要我們定位到具體報錯的代碼位置了,去發現并定位混淆配置錯誤的代碼。Android Studio在debug模式下運行的程序,是沒有經過ProGuard混淆配置的,所以我們沒法通過debug模式定位混淆出錯的位置。但是我們該如何定位到代碼崩潰的代碼位置了,解決的方法就是在debug模式下,我們也讓程序跑ProGuard文件,進行debug模式下的混淆打包。
buildTypes {
release {
//混淆
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug{
//混淆
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
這樣,在debug模式下,就可以定位到程序崩潰或異常的代碼了,進而可以查詢到混淆配置錯誤的代碼進行修改調整。
好了,以上就是我在項目中配置ProGuard文件的經驗總結,希望大家也能針對自己的項目做一份特制的ProGuard文件,讓apk瘦身成功。
來自:http://www.androidchina.net/5678.html