編輯:關於Android編程
前面一篇文章中我們講解了android裡面的多渠道打包,對於大型的app來說,幾百個上千個渠道包都是很正常的事,所以效率定制化是一件很重要的事。主要講解了三種多渠道打包方式,並分析了其各自的利弊,在各自產品多渠道打包的時候,可以根據自身的產品需求選擇相應的打包方式。
而本文主要講解Apk的混淆,這裡的混淆分為兩種代碼混淆和資源文件混淆。實際的產品研發中為了防止自己的勞動成果被別人竊取,混淆代碼能有效防止apk文件被反編譯,進而查看源代碼。說來慚愧,作為互聯網創業公司的我們也確實對競品Apk反編譯研究過,如果Apk混淆之後確實對理解源碼的業務流程造成了困擾,這也從側面說明了Apk混淆的重要性。
所以對於android apk安裝文件來說如何混淆代碼實現對apk文件的保護是一個很重要的問題,而android提供了Progurd方式來混淆apk中的代碼,其核心的邏輯是在代碼層將一些易懂的源代碼類名,方法名稱替換成毫無意義的a、b、c、d…,這樣當別人反編譯出你的Apk文件時,看到的源代碼也無法還原其本身的邏輯。
下面我們將分別介紹代碼混淆與資源文件混淆具體實踐。
- 代碼混淆-Progurd
下面來總結以下混淆代碼的步驟:
在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
}
}
找到項目中的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 ;
}
-keepclasseswithmembernames class * { # 保持自定義控件不被混淆
public (android.content.Context, android.util.AttributeSet);
}
-keepclasseswithmembernames class * { # 保持自定義控件不被混淆
public (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 ;
}
-keepclasseswithmembernames class * {
public (android.content.Context, android.util.AttributeSet);
}
-keepclasseswithmembernames class * {
public (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 (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 ;
}
(5)混淆配置完成之後編譯混淆包,測試
有的時候混淆之後可能會出現一些奇形怪狀的bug,有條件的話,可以讓QA回滾一次混淆包的測試。
- 資源文件混淆-微信方案
前面我們說過本文主要講的是Apk的混淆,除了源代碼的混淆,還有資源文件的混淆。
去年微信推出了一個apk資源混淆方案,該方案的具體原理課參見:安裝包立減1M–微信Android資源混淆打包工具
在其文章中分析了資源文件混淆的幾種方案:
方案一:最簡單的方法,我們按照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的混淆能有一個整體的認識。
最開始使用AndroidStudio的時候,各種不適應,各種懷戀Eclipse,寫了幾千行代碼勉強熟悉了AndroidStudio後,感覺AndroidStudio不要太
推薦閱讀:先給大家分享一下,側滑刪除,布局也就是前面一個item,然後有兩個隱藏的按鈕(TextView也可以),然後我們可以向左側滑動,然後顯示出來,然後對delete
適配:即當前應用在相同的手機上面顯示相同的效果。適配前需要首先確定當前手機所屬像素密度類型(如:xhdpi、hdpi、mdpi等) 像素密度:每英寸上分布的像素點個數,單
LinearLayout是Android中最常用的布局之一,它將自己包含的子元素按照一個方向進行排列。方向有兩種,水平或者豎直。這個方向可以通過設置android:ori