前面一篇文章中我們講解了android里面的多渠道打包,對于大型的app來說,幾百個上千個渠道包都是很正常的事,所以效率定制化是一件很重要的事。主要講解了三種多渠道打包方式,并分析了其各自的利弊,在各自產品多渠道打包的時候,可以根據自身的產品需求選擇相應的打包方式。具體可參考:[Android產品研發(五)–>多渠道打包](http://blog.csdn.net/qq_23547831/article/details/51569261)。
而本文主要講解Apk的混淆,這里的混淆分為兩種代碼混淆和資源文件混淆。實際的產品研發中為了防止自己的勞動成果被別人竊取,混淆代碼能有效防止apk文件被反編譯,進而查看源代碼。說來慚愧,作為互聯網創業公司的我們也確實對競品Apk反編譯研究過,如果Apk混淆之后確實對理解源碼的業務流程造成了困擾,這也從側面說明了Apk混淆的重要性。
所以對于android apk安裝文件來說如何混淆代碼實現對apk文件的保護是一個很重要的問題,而android提供了Progurd方式來混淆apk中的代碼,其核心的邏輯是在代碼層將一些易懂的源代碼類名,方法名稱替換成毫無意義的a、b、c、d...,這樣當別人反編譯出你的Apk文件時,看到的源代碼也無法還原其本身的邏輯。
**下面我們將分別介紹代碼混淆與資源文件混淆具體實踐。**
**代碼混淆-Progurd**
下面來總結以下混淆代碼的步驟:
1. 在android studio的android項目中找到module的gradle配置文件,添加proguard配置
~~~
buildTypes {
debug {
// 顯示Log
buildConfigField "boolean", "LOG_DEBUG", "true"
//混淆
minifyEnabled false
//Zipalign優化
zipAlignEnabled true
// 移除無用的resource文件
shrinkResources true
//加載默認混淆配置文件
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
//簽名
signingConfig signingConfigs.debug
}
release {
// 不顯示Log
buildConfigField "boolean", "LOG_DEBUG", "false"
//混淆
minifyEnabled true
//Zipalign優化
zipAlignEnabled true
// 移除無用的resource文件
shrinkResources true
//加載默認混淆配置文件
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
//簽名
signingConfig signingConfigs.relealse
}
}
~~~
2. 找到項目中的proguard-rules.pro文件,該文件就是我們的混淆配置文件

3. 編寫proguard-rules.pro文件,添加混淆配置
(1)proguard混淆語法
~~~
-libraryjars class_path 應用的依賴包,如android-support-v4
-keep [,modifier,...] class_specification 這里的keep就是保持的意思,意味著不混淆某些類
-keepclassmembers [,modifier,...] class_specification 同樣的保持,不混淆類的成員
-keepclasseswithmembers [,modifier,...] class_specification 不混淆類及其成員
-keepnames class_specification 不混淆類及其成員名
-keepclassmembernames class_specification 不混淆類的成員名
-keepclasseswithmembernames class_specification 不混淆類及其成員名
-assumenosideeffects class_specification 假設調用不產生任何影響,在proguard代碼優化時會將該調用remove掉。如system.out.println和Log.v等等
-dontwarn [class_filter] 不提示warnning
~~~
(2)混淆原則
~~~
jni方法不可混淆
反射用到的類不混淆(否則反射可能出現問題)
AndroidMainfest中的類不混淆,四大組件和Application的子類和Framework層下所有的類默認不會進行混淆
Parcelable的子類和Creator靜態成員變量不混淆,否則會產生android.os.BadParcelableException異常
使用GSON、fastjson等框架時,所寫的JSON對象類不混淆,否則無法將JSON解析成對應的對象
使用第三方開源庫或者引用其他第三方的SDK包時,需要在混淆文件中加入對應的混淆規則
有用到WEBView的JS調用也需要保證寫的接口方法不混淆
~~~
(3)第三方庫的混淆原則
一般的第三方庫都有自身的混淆方案,可直接引用其自身的混淆配置即可
若無混淆配置,一般的可配置不混淆第三方庫
(4)最后帖上我們項目中實際的混淆方案
~~~
# Glide圖片庫的混淆處理
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
**[] $VALUES;
public *;
}
-optimizationpasses 5
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontpreverify
-verbose
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
# 高德地圖混淆腳本
-keep class com.android.support.**{ *; }
-keep interface android.support.v4.app.**{ *; }
-keep public class * extends android.support.v4.**
-keep public class * extends android.app.Fragment
-dontwarn com.amap.api.**
-dontwarn com.a.a.**
-dontwarn com.autonavi.**
-keep class com.amap.api.** {*;}
-keep class com.autonavi.** {*;}
-keep class com.a.a.** {*;}
# Gson混淆腳本
-keep class com.google.gson.stream.** {*;}
-keep class com.youyou.uuelectric.renter.Network.user.** {*;}
# butterknife混淆腳本
-dontwarn butterknife.internal.**
-keep class **$$ViewInjector { *; }
-keepnames class * { @butterknife.InjectView *;}
# -------------系統類不需要混淆 --------------------------
-keep public class * extends android.app.Fragment
-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.support.**
-keep public class com.android.vending.licensing.ILicensingService
-keepclasseswithmembernames class * { # 保持native方法不被混淆
native <methods>;
}
-keepclasseswithmembernames class * { # 保持自定義控件不被混淆
public <init>(android.content.Context, android.util.AttributeSet);
}
-keepclasseswithmembernames class * { # 保持自定義控件不被混淆
public <init>(android.content.Context, android.util.AttributeSet, int);
}
-keepclassmembers enum * { # 保持枚舉enum類不被混淆
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keep class * implements android.os.Parcelable { # 保持Parcelable不被混淆
public static final android.os.Parcelable$Creator *;
}
# --------- 忽略異常提示 --------------------
-dontwarn butterknife.internal.**
-dontwarn com.alipay.**
-dontwarn com.mikepenz.**
-dontwarn org.apache.**
-dontwarn com.amap.**
-dontwarn com.android.volley.**
-dontwarn com.rey.**
-dontwarn com.testin.**
-dontwarn jp.wasabeef.**
# ---------- 保持代碼 --------------
-keep class com.youyou.uuelectric.renter.Utils.** {*;}
-keep class it.neokree.** {*;}
-keep class org.apache.** {*;}
-keep class com.iflytek.** {*;}
-keep class com.google.protobuf.** { *; }
-keep class com.youyou.uuelectric.renter.pay.** {*;}
# ---------------- eventbus避免混淆 ------------
-keepclassmembers class ** {
public void onEvent*(**);
void onEvent*(**);
}
# --------------- 友盟統計避免混淆 -------------------------
-dontwarn android.support.v4.**
-dontwarn org.apache.commons.net.**
-dontwarn com.tencent.**
-keepclasseswithmembernames class * {
native <methods>;
}
-keepclasseswithmembernames class * {
public <init>(android.content.Context, android.util.AttributeSet);
}
-keepclasseswithmembernames class * {
public <init>(android.content.Context, android.util.AttributeSet, int);
}
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
-keepclasseswithmembers class * {
public <init>(android.content.Context);
}
-dontshrink
-dontoptimize
-dontwarn com.google.android.maps.**
-dontwarn android.webkit.WebView
-dontwarn com.umeng.**
-dontwarn com.tencent.weibo.sdk.**
-dontwarn com.facebook.**
-keep enum com.facebook.**
-keepattributes Exceptions,InnerClasses,Signature
-keepattributes *Annotation*
-keepattributes SourceFile,LineNumberTable
-keep public interface com.facebook.**
-keep public interface com.tencent.**
-keep public interface com.umeng.socialize.**
-keep public interface com.umeng.socialize.sensor.**
-keep public interface com.umeng.scrshot.**
-keep public class com.umeng.socialize.* {*;}
-keep public class javax.**
-keep public class android.webkit.**
-keep class com.facebook.**
-keep class com.umeng.scrshot.**
-keep public class com.tencent.** {*;}
-keep class com.umeng.socialize.sensor.**
-keep class com.tencent.mm.sdk.openapi.WXMediaMessage {*;}
-keep class com.tencent.mm.sdk.openapi.** implements com.tencent.mm.sdk.openapi.WXMediaMessage$IMediaObject {*;}
-keep class im.yixin.sdk.api.YXMessage {*;}
-keep class im.yixin.sdk.api.** implements im.yixin.sdk.api.YXMessage$YXMessageData{*;}
-keep public class [your_pkg].R$*{
public static final int *;
}
# 熱修復混淆
-keep class * extends java.lang.annotation.Annotation
-keep class com.alipay.euler.andfix.** { *; }
-keepclasseswithmembernames class * {
native <methods>;
}
~~~
(5)混淆配置完成之后編譯混淆包,測試
有的時候混淆之后可能會出現一些奇形怪狀的bug,有條件的話,可以讓QA回滾一次混淆包的測試。
**資源文件混淆-微信方案**
前面我們說過本文主要講的是Apk的混淆,除了源代碼的混淆,還有資源文件的混淆。
去年微信推出了一個apk資源混淆方案,該方案的具體原理課參見:[安裝包立減1M--微信Android資源混淆打包工具](http://mp.weixin.qq.com/s?__biz=MzAwNDY1ODY2OQ==&mid=208135658&idx=1&sn=ac9bd6b4927e9e82f9fa14e396183a8f#rd)
在其文章中分析了資源文件混淆的幾種方案:
* 方案一:最簡單的方法,我們按照Proguard的做法,直接在源碼級別修改,將代碼以及xml的R.string.name中替換到R.string.a,icon.png重命名為a.png 然后再交給Android編譯。
* 方案二:根據Android的編譯流程,所有資源ID已經被編譯成32位int值。這說明我們并不需要去修改xml與java,因為在編譯過程已經被R.java所替換,我們直接修改resources.arsc的二進制數據,不改變打包流程,只要在生成resources.arsc之后修改它,同時重命名資源文件。
* 方案三:直接處理安裝包. 不依賴源碼,不依賴編譯過程,僅僅輸入一個安裝包,得到一個混淆包。
幾種方案的對比如下:

微信版apk資源混淆方案采用的就是方案三,其具體的實現原理可參見其微信公眾號中的介紹;
利用該方案我們可以實現對資源文件的混淆,將apk中所有的資源文件名稱都替換為a、b、c、d...,這樣也從側面增加了不良人員反編譯apk的難度,同時也減少了apk的大小,有興趣的同學可以自己嘗試一下。
**總結**: 上面我們分析了兩種混淆方式代碼混淆和資源文件混淆,其都是通過對源代碼或者資源文件名稱混淆(將其名稱替換成無意義的名稱)增加反編譯的難度,減小Apk的大小,因此對產品而言這項工作還是很有意義的,一般而言都是做到了源代碼混淆,而對資源文件混淆方面意識不足,希望大家通過閱讀本文對Apk的混淆能有一個整體的認識。
另外對產品研發技術,技巧,實踐方面感興趣的同學可以參考我的:
[android產品研發(一)-->實用開發規范 ](http://blog.csdn.net/qq_23547831/article/details/51534013)
[android產品研發(二)-->啟動頁優化 ](http://blog.csdn.net/qq_23547831/article/details/51541277)
[android產品研發(三)-->基類Activity ](http://blog.csdn.net/qq_23547831/article/details/51546974)
[android產品研發(四)-->減小Apk大小](http://blog.csdn.net/qq_23547831/article/details/51559066)
[android產品研發(五)-->多渠道打包](http://blog.csdn.net/qq_23547831/article/details/51569261)
- 前言
- (一)–>實用開發規范
- (二)-->啟動頁優化
- (三)-->基類Activity
- (四)-->減小Apk大小
- (五)-->多渠道打包
- (六)-->Apk混淆
- (七)-->Apk熱修復
- (八)-->App數據統計
- (九)-->App網絡數據解析
- (十)-->盡量不使用靜態變量保存數據
- (十一)-->應用內跳轉Scheme協議
- (十二)-->App長連接實現
- (十三)-->App輪詢操作
- (十四)-->App升級與更新
- (十五)-->內存對象序列化
- (十六)-->開發者選項
- (十七)-->Hybrid開發
- (十八)-->webview問題集錦
- (十九)-->Android studio中的單元測試
- (二十)-->代碼Review
- (二十一)-->Android中的UI優化
- (二十二)-->Android實用調試技巧
- (二十三)-->Android中保存靜態秘鑰實踐
- (二十四)-->內存泄露場景與檢測
- (二十五)-->MVC/MVVM/MVP簡單理解