Android上線混淆打包:你要的混淆配置都在這(採坑持續更新)
本文導語:
Android專案混淆打包,相信大家並不陌生,即使是沒有過獨立開發經驗的小夥伴,打包上線,也是我們必須要掌握的專業技能。本篇文章是我將開發過中用到的一些混淆配置,進行了分類的整理,肯定不能保證足夠全面,但是對於一個獨立專案用到的混淆配置,基本都能囊括到,還算是比較通用,特地整理出來供大家學習參考。
我們可能經常會遇到這種情況,debug模式下直接執行專案跑得好好的,但是打包後,卻報一些崩潰異常。所以在文末,我整理一些採坑的經驗總結,歡迎有遇到不同的採坑經驗的在部落格下評論分享,一起總結學習,避免以後少走彎路。
對於程式碼混淆技術的簡介,推薦大家可以參考學習:ofollow,noindex">ProGuard程式碼混淆技術詳解 ,下面開始介紹專案混淆的配置。
《一》通用的混淆-基本配置
#-----------基本配置-------------- # 程式碼混淆壓縮比,在0~7之間,預設為5,一般不需要改 -optimizationpasses 5 # 混淆時不使用大小寫混合,混淆後的類名為小寫 -dontusemixedcaseclassnames # 指定不去忽略非公共的庫的類 -dontskipnonpubliclibraryclasses # 指定不去忽略非公共的庫的類的成員 -dontskipnonpubliclibraryclassmembers # 不做預校驗,可加快混淆速度 # preverify是proguard的4個步驟之一 # Android不需要preverify,去掉這一步可以加快混淆速度 -dontpreverify # 不優化輸入的類檔案 -dontoptimize # 混淆時生成日誌檔案,即對映檔案 -verbose # 指定對映檔案的名稱 -printmapping proguardMapping.txt #混淆時所採用的演算法 -optimizations !code/simplification/arithmetic,!field/*,!class/merging/* # 保護程式碼中的Annotation不被混淆 -keepattributes *Annotation* # 忽略警告 -ignorewarning # 保護泛型不被混淆 -keepattributes Signature # 丟擲異常時保留程式碼行號 -keepattributes SourceFile,LineNumberTable #-----------需要保留的東西-------------- # 保留所有的本地native方法不被混淆 -keepclasseswithmembernames class * { native <methods>; } # 保留了繼承自Activity、Application、Fragment這些類的子類 -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.view.View -keep public class com.android.vending.licensing.ILicensingService # support-v4 -dontwarn android.support.v4.** -keep class android.support.v4.** { *; } -keep interface android.support.v4.** { *; } -keep public class * extends android.support.v4.** # support-v7 -dontwarn android.support.v7.**#去掉警告 -keep class android.support.v7.** { *; }#過濾android.support.v7 -keep interface android.support.v7.app.** { *; } -keep public class * extends android.support.v7.** #----------------保護指定的類和類的成員,但條件是所有指定的類和類成員是要存在------------------------------------ -keepclasseswithmembernames class * { public <init>(android.content.Context, android.util.AttributeSet); } -keepclasseswithmembernames class * { public <init>(android.content.Context, android.util.AttributeSet, int); } # 保持自定義控制元件類不被混淆,指定格式的構造方法不去混淆 -keepclasseswithmembers class * { public <init>(android.content.Context); public <init>(android.content.Context, android.util.AttributeSet); public <init>(android.content.Context, android.util.AttributeSet, int); } # 保持自定義控制元件類不被混淆 -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*(); } # 保留在Activity中的方法引數是View的方法 # 從而我們在layout裡邊編寫onClick就不會被影響 -keepclassmembers class * extends android.app.Activity { public void *(android.view.View); } # 保留列舉 enum 類不被混淆 -keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String); } # 保留 Parcelable 不被混淆 -keep class * implements android.os.Parcelable { public static final android.os.Parcelable$Creator *; } # 保留 Serializable 不被混淆 -keepnames class * implements java.io.Serializable -keepclassmembers class * implements java.io.Serializable { static final long serialVersionUID; private static final java.io.ObjectStreamField[] serialPersistentFields; !static !transient <fields>; !private <fields>; !private <methods>; private void writeObject(java.io.ObjectOutputStream); private void readObject(java.io.ObjectInputStream); java.lang.Object writeReplace(); java.lang.Object readResolve(); } # 不混淆資源類 -keepclassmembers class **.R$* { *; } # 對於帶有回撥函式onXXEvent()的,不能被混淆 -keepclassmembers class * { void *(**On*Event); } # WebView -keepclassmembers class fqcn.of.javascript.interface.for.Webview { public *; } -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, jav.lang.String); } # 保留實體類和成員不被混淆(根據具體情況修改entity的路徑) -keep class com.smart.tvpos.bean.**{*;}
《二》常用的第三方類庫的混淆新增
#eventbus -keepattributes *Annotation* -keepclassmembers class ** { @de.greenrobot.event.Subscribe <methods>; } -keep enum org.greenrobot.eventbus.ThreadMode { *; } -keepclassmembers class ** { @org.greenrobot.eventbus.Subscribe <methods>; } -keep enum de.greenrobot.event.ThreadMode { *; } #過濾fastjson -keep class com.alibaba.fastjson.** { *; } -dontwarn com.alibaba.fastjson.** #過濾okhttp -dontwarn com.squareup.okhttp3.** -keep class com.squareup.okhttp3.** { *;} -dontwarn okio.** #過濾glide -keep public class * implements com.bumptech.glide.module.GlideModule -keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** { **[] $VALUES; public *; } #retrofit -dontwarn okio.** -dontwarn javax.annotation.** #解決使用Retrofit+rxJava聯網時,在6.0系統出現java.lang.InternalError奔潰的問題:http://blog.csdn.net/mp624183768/article/details/79242147 -keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* { long producerIndex; long consumerIndex; } -keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef { rx.internal.util.atomic.LinkedQueueNode producerNode; } -keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef { rx.internal.util.atomic.LinkedQueueNode consumerNode; } #過濾butterknife專案 -keep class butterknife.** { *; } -dontwarn butterknife.internal.** -keep class **$$ViewBinder { *; } -keepclasseswithmembernames class * { @butterknife.* <fields>; } -keepclasseswithmembernames class * { @butterknife.* <methods>; } #greendao3.2.0,此是針對3.2.0,如果是之前的,可能需要更換下包名 -keep class org.greenrobot.greendao.**{*;} -keepclassmembers class * extends org.greenrobot.greendao.AbstractDao { public static java.lang.String TABLENAME; } -keep class **$Properties
《三》常用的第三方SDK的混淆配置
#友盟分享需要混淆的處理 -dontshrink -dontoptimize -dontwarn com.google.android.maps.** -dontwarn android.webkit.WebView -dontwarn com.umeng.** -dontwarn com.tencent.weibo.sdk.** -dontwarn com.facebook.** -keep public class javax.** -keep public class android.webkit.** -dontwarn android.support.v4.** -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 class com.facebook.** -keep class com.facebook.** { *; } -keep class com.umeng.scrshot.** -keep public class com.tencent.** {*;} -keep class com.umeng.socialize.sensor.** -keep class com.umeng.socialize.handler.** -keep class com.umeng.socialize.handler.* -keep class com.umeng.weixin.handler.** -keep class com.umeng.weixin.handler.* -keep class com.umeng.qq.handler.** -keep class com.umeng.qq.handler.* -keep class UMMoreHandler{*;} -keep class com.tencent.mm.sdk.modelmsg.WXMediaMessage {*;} -keep class com.tencent.mm.sdk.modelmsg.** implements com.tencent.mm.sdk.modelmsg.WXMediaMessage$IMediaObject {*;} -keep class im.yixin.sdk.api.YXMessage {*;} -keep class im.yixin.sdk.api.** implements im.yixin.sdk.api.YXMessage$YXMessageData{*;} -keep class com.tencent.mm.sdk.** { *; } -keep class com.tencent.mm.opensdk.** { *; } -keep class com.tencent.wxop.** { *; } -keep class com.tencent.mm.sdk.** { *; } -dontwarn twitter4j.** -keep class twitter4j.** { *; } -keep class com.tencent.** {*;} -dontwarn com.tencent.** -keep class com.kakao.** {*;} -dontwarn com.kakao.** -keep public class com.umeng.com.umeng.soexample.R$*{ public static final int *; } -keep public class com.linkedin.android.mobilesdk.R$*{ public static final int *; } -keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String); } -keep class com.tencent.open.TDialog$* -keep class com.tencent.open.TDialog$* {*;} -keep class com.tencent.open.PKDialog -keep class com.tencent.open.PKDialog {*;} -keep class com.tencent.open.PKDialog$* -keep class com.tencent.open.PKDialog$* {*;} -keep class com.umeng.socialize.impl.ImageImpl {*;} -keep class com.sina.** {*;} -dontwarn com.sina.** -keep classcom.alipay.share.sdk.** { *; } -keepnames class * implements android.os.Parcelable { public static final ** CREATOR; } -keep class com.linkedin.** { *; } -keep class com.android.dingtalk.share.ddsharemodule.** { *; } -keepattributes Signature # 友盟統計 -keep class com.umeng.commonsdk.** {*;} -keepclassmembers class * { public <init> (org.json.JSONObject); } -keep public class [com.zongxueguan.naochanle_android].R$*{ public static final int *; } -keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String); } ### 網易雲信聊天:NIM SDK -dontwarn com.netease.** -keep class com.netease.** {*;} -dontwarn org.apache.lucene.** -keep class org.apache.lucene.** {*;} -keep class net.sqlcipher.** {*;} -keepclasseswithmembernames class * { native <methods>; } -keepclasseswithmembers class * { public <init>(android.content.Context, android.util.AttributeSet); 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 *; } -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(); } -keep class **.R$* {*;} #微信支付混淆 -keep class com.tencent.mm.opensdk.** { *; } -keep class com.tencent.wxop.** { *; } -keep class com.tencent.mm.sdk.** { *; } #JPush配置 混淆 -dontoptimize -dontpreverify -dontwarn cn.jpush.** -keep class cn.jpush.** { *; } -keep class * extends cn.jpush.android.helpers.JPushMessageReceiver { *; } -dontwarn cn.jiguang.** -keep class cn.jiguang.** { *; } #高德地圖混淆配置 # 3D 地圖 V5.0.0之前: -keepclass com.amap.api.maps.**{*;} -keepclass com.autonavi.amap.mapcore.*{*;} -keepclass com.amap.api.trace.**{*;} #3D 地圖 V5.0.0之後: -keepclass com.amap.api.maps.**{*;} -keepclass com.autonavi.**{*;} -keepclass com.amap.api.trace.**{*;} #定位 -keep class com.amap.api.location.**{*;} -keep class com.amap.api.fence.**{*;} -keep class com.autonavi.aps.amapapi.model.**{*;} # 搜尋 -keepclass com.amap.api.services.**{*;} # 2D地圖 -keep class com.amap.api.maps2d.**{*;} -keep class com.amap.api.mapcore2d.**{*;} #導航 -keep class com.amap.api.navi.**{*;} -keep class com.autonavi.**{*;}
《四》其他
# 對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) } # 保留JS方法不被混淆 -keepclassmembers class com.example.xxx.MainActivity$JSInterface1 { <methods>; }
《五》打包簽名配置:
app 的build.gradle
//簽名檔案資訊配置 signingConfigs { config { storeFile file(KEY_PATH) storePassword KEY_PASS keyAlias ALIAS_NAME keyPassword ALIAS_PASS } } buildTypes { debug { debuggable true minifyEnabled false } release { //開啟程式碼混淆 minifyEnabled true //Zipalign優化 zipAlignEnabled true // 移除無用的resource檔案 shrinkResources true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' signingConfig signingConfigs.config } }
gradle.properties
KEY_PATH=C:/develop/app.keystore KEY_PASS= keystroepwd ALIAS_NAME=aliasname ALIAS_PASS=keystroepwd
《六》常遇到的程式碼混淆後的崩潰問題彙總(持續更新)
(1)GreenDao 3.2.0 報異常的混淆配置。
#-keep class de.greenrobot.event.** {*;} #-keep class de.greenrobot.** {*;} ##保持greenDao的方法不被混淆 ##用來保持生成的表名不被混淆 #-keepclassmembers class * extends de.greenrobot.dao.AbstractDao { #public static java.lang.String TABLENAME; #} #-keep class **$Properties
崩潰-資訊如下:
Process: com.smart.novel, PID: 16504 org.greenrobot.greendao.DaoException: Could not init DAOConfig at org.greenrobot.greendao.b.a.<init>(DaoConfig.java:94) at org.greenrobot.greendao.b.a(AbstractDaoMaster.java:44) at com.smart.novel.db.gen.a.<init>(DaoMaster.java:54) at com.smart.novel.db.a.a.<init>(DbManager.java:33)
解決:將GreenDao的混淆配置修改為如下即可
#-keep class de.greenrobot.event.** {*;} #-keep class de.greenrobot.** {*;} ##保持greenDao的方法不被混淆 ##用來保持生成的表名不被混淆 #-keepclassmembers class * extends de.greenrobot.dao.AbstractDao { #public static java.lang.String TABLENAME; #} #-keep class **$Properties
(2)使用MVP架構時,使用如下反射的方法建立類物件,會丟擲 java.lang.Object cannot be cast to com.smart.framework.library.base.mvp.BaseModel的異常:
public static <T> T getT(Object o, int i) { try { return ((Class<T>) ((ParameterizedType) (o.getClass() .getGenericSuperclass())).getActualTypeArguments()[i]) .newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (ClassCastException e) { e.printStackTrace(); } return null; }
報如下奔潰異常資訊:
Caused by: java.lang.ClassCastException: java.lang.Object cannot be cast to com.smart.framework.library.base.mvp.BaseModel at com.smart.framework.library.base.BaseMVPFragment.initViewsAndEvents(BaseMVPFragment.java:24) at com.smart.framework.library.base.BaseLazyFragment.onViewCreated(BaseLazyFragment.java:118) at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1132) at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1295) at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1277) at android.support.v4.app.FragmentManagerImpl.dispatchActivityCreated(FragmentManager.java:2154) at android.support.v4.app.FragmentController.dispatchActivityCreated(FragmentController.java:201) at android.support.v4.app.FragmentActivity.onStart(FragmentActivity.java:603) at android.support.v7.app.AppCompatActivity.onStart(AppCompatActivity.java:181) at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1340) at android.app.Activity.performStart(Activity.java:7191)
解決:把你使用MVP的地方涉及到泛型反射獲取物件相關的類,忽略混淆。包括Model 和Presenter,BaseActivity和BaseFragment等,保持不被混淆即可。 參考:MVPHelper外掛之混淆報錯
#保留mvp中的Model 和Presenter 解決混淆打包後報:Caused by: java.lang.ClassCastException: java.lang.Object cannot be cast to com.smart.framework.library.base.mvp.BaseModel -keep class com.smart.novel.mvp.** { *; } -keep class com.smart.framework.library.base.** { *; }
發現還崩潰。。。。。。
09-11 16:24:00.465 24788-24788/? W/Activity: AppLock checkAppLockState locked:false verifying:false pkgName = com.smart.novel isInMultiWindowMode:false showWhenLocked:false 09-11 16:24:00.466 24788-24788/? W/System.err: java.lang.IllegalArgumentException: Parameter specified as non-null is null: method kotlin.jvm.internal.e.b, parameter result 09-11 16:24:00.466 24788-24788/? W/System.err:at com.smart.novel.mvp.presenter.BookShelfPresenter$getBookShelfData$1.onSuccess(Unknown Source:2) 09-11 16:24:00.466 24788-24788/? W/System.err:at com.smart.novel.mvp.presenter.BookShelfPresenter$getBookShelfData$1.onSuccess(BookShelfPresenter.kt:17) 09-11 16:24:00.466 24788-24788/? W/System.err:at com.smart.novel.b.e.a(RetrofitRxManager.kt:346) 09-11 16:24:00.466 24788-24788/? W/System.err:at com.smart.novel.b.e.onNext(RetrofitRxManager.kt:343) 09-11 16:24:00.466 24788-24788/? W/System.err:at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.drainNormal(ObservableObserveOn.java:200) 09-11 16:24:00.466 24788-24788/? W/System.err:at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.run(ObservableObserveOn.java:252) 09-11 16:24:00.466 24788-24788/? W/System.err:at io.reactivex.a.b.f.run(HandlerScheduler.java:109) 09-11 16:24:00.466 24788-24788/? W/System.err:at android.os.Handler.handleCallback(Handler.java:789) 09-11 16:24:00.466 24788-24788/? W/System.err:at android.os.Handler.dispatchMessage(Handler.java:98) 09-11 16:24:00.466 24788-24788/? W/System.err:at android.os.Looper.loop(Looper.java:164) 09-11 16:24:00.466 24788-24788/? W/System.err:at android.app.ActivityThread.main(ActivityThread.java:6944) 09-11 16:24:00.466 24788-24788/? W/System.err:at java.lang.reflect.Method.invoke(Native Method) 09-11 16:24:00.466 24788-24788/? W/System.err:at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327) 09-11 16:24:00.466 24788-24788/? W/System.err:at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374) 09-11 16:24:00.511 1489-4484/? W/ActivityManager: crash : com.smart.novel,0 09-11 16:24:00.512 1489-4484/? W/ActivityManager:Force finishing activity com.smart.novel/.ui.ACT_Home
報出的問題是Retrofit的問題,不知道是不是抽取了泛型的原因。然後我把net包下關於Retrofit聯網的類去掉混淆,就好了
解決:混淆檔案中去掉對Retrofit聯網相關的類的混淆
#-keep class com.smart.novel.net.** { *; }
(3)專案使用Retrofit+RxJava聯網時,在6.0系統會出現java.lang.InternalErro異常,解決如下:
#解決使用Retrofit+rxJava聯網時,在6.0系統出現java.lang.InternalError奔潰的問題:http://blog.csdn.net/mp624183768/article/details/79242147 -keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* { long producerIndex; long consumerIndex; } -keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef { rx.internal.util.atomic.LinkedQueueNode producerNode; } -keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef { rx.internal.util.atomic.LinkedQueueNode consumerNode; }