編輯:關於Android編程
Java 是一種跨平台的、解釋型語言,Java 源代碼編譯成中間”字節碼”存儲於 class 文件中。由於跨平台的需要,Java 字節碼中包括了很多源代碼信息,如變量名、方法名,並且通過這些名稱來訪問變量和方法,這些符號帶有許多語義信息,很容易被反編譯成 Java 源代碼。為了防止這種現象,我們可以使用 Java 混淆器對 Java 字節碼進行混淆。
混淆就是對發布出去的程序進行重新組織和處理,使得處理後的代碼與處理前代碼完成相同的功能,而混淆後的代碼很難被反編譯,即使反編譯成功也很難得出程序的真正語義。被混淆過的程序代碼,仍然遵照原來的檔案格式和指令集,執行結果也與混淆前一樣,只是混淆器將代碼中的所有變量、函數、類的名稱變為簡短的英文字母代號,在缺乏相應的函數名和程序注釋的況下,即使被反編譯,也將難以閱讀。同時混淆是不可逆的,在混淆的過程中一些不影響正常運行的信息將永久丟失,這些信息的丟失使程序變得更加難以理解。
-include {filename} 從給定的文件中讀取配置參數
-basedirectory {directoryname} 指定基礎目錄為以後相對的檔案名稱
-injars {class_path} 指定要處理的應用程序jar,war,ear和目錄
-outjars {class_path} 指定處理完後要輸出的jar,war,ear和目錄的名稱
-libraryjars {classpath} 指定要處理的應用程序jar,war,ear和目錄所需要的程序庫文件
-dontskipnonpubliclibraryclasses 指定不去忽略非公共的庫類
-dontskipnonpubliclibraryclassmembers 指定不去忽略包可見的庫類的成員
保留選項
-keep {Modifier} {class_specification} 保護指定的類文件和類的成員
-keepnames {class_specification} 保護指定的類和類的成員的名稱(如果他們不會在壓縮步驟中刪除)
-keepclassmembers {modifier} {class_specification} 保護指定類的成員,如果此類受到保護他們會保護的更好
-keepclassmembernames {class_specification} 保護指定的類的成員的名稱(如果他們不會在壓縮步驟中刪除)
-keepclasseswithmembers {class_specification} 保護指定的類和類的成員,但條件是所有指定的類和類成員是要存在
-keepclasseswithmembernames {class_specification} 保護指定的類和類的成員的名稱,如果所有指定的類成員出席(在壓縮步驟之後)
-printseeds {filename} 列出類和類的成員-keep選項的清單,標准輸出到給定的文件
-keepattributes {attribute_name,...} 保護給定的可選屬性,例如LineNumberTable, LocalVariableTable, SourceFile, Deprecated, Synthetic, Signature, and InnerClasses.
壓縮相關
-dontshrink 不壓縮輸入的類文件
-printusage {filename}
-dontwarn 如果有警告也不終止
-whyareyoukeeping {class_specification}
優化相關
-dontoptimize 不優化輸入的類文件
-assumenosideeffects {class_specification} 優化時假設指定的方法,沒有任何副作用
-allowaccessmodification 優化時允許訪問並修改有修飾符的類和類的成員
混淆相關
-dontobfuscate 不混淆輸入的類文件
-printmapping {filename}
-applymapping {filename} 重用映射增加混淆
-obfuscationdictionary {filename} 使用給定文件中的關鍵字作為要混淆方法的名稱
-overloadaggressively 混淆時應用侵入式重載
-useuniqueclassmembernames 確定統一的混淆類的成員名稱來增加混淆
-flattenpackagehierarchy {package_name} 重新包裝所有重命名的包並放在給定的單一包中
-repackageclass {package_name} 重新包裝所有重命名的類文件中放在給定的單一包中
-dontusemixedcaseclassnames 混淆時不會產生形形色色的類名
-renamesourcefileattribute {string} 設置源文件中給定的字符串常量
*
匹配任意長度字符,但不含包名分隔符(.)。比如說我們的完整類名是com.vise.note.MainActivity
,使用com.*
,或者com.vise.*
都是無法匹配的,因為*
無法匹配包名中的分隔符,正確的匹配方式是com.vise.*.*
,或者com.vise.note.*
,這些都是可以的。但如果你不寫任何其它內容,只有一個*
,那就表示匹配所有的東西。
**
匹配任意長度字符,並且包含包名分隔符(.)
。比如proguard-android.txt
中使用的-dontwarn android.support.**
就可以匹配android.support
包下的所有內容,包括任意長度的子包。
***
匹配任意參數類型。比如void set*(***)
就能匹配任意傳入的參數類型,*** get*()
就能匹配任意返回值的類型。
…
匹配任意長度的任意類型參數。比如void test(…)
就能匹配任意void test(String a)
或者是void test(int a, String b)
這些方法。
混淆是apk上線前挺重要的一個環節,Android使用的是ProGuard,可以起到壓縮,混淆,預檢,優化的作用。縱觀大部分項目的混淆文件,其大部分內容都是固定的,從中可以整理出一個通用的模板,模板內容大致分為以下幾個部分:基本指令、公共組件、第三方庫、實體類、反射相關及JS調用相關。其中前兩部分基本不會有太大變化,第三方庫網上基本都會提供混淆方式,下文也會依據網上資源整理出大部分的三方庫保留方式,而後面幾個部分就與具體項目相關了,掌握思路後依照具體項目定制就行。
3.1、基本指令
-optimizationpasses 5 # 指定代碼的壓縮級別
-dontusemixedcaseclassnames # 表示混淆時不使用大小寫混合類名
-dontskipnonpubliclibraryclasses # 表示不跳過library中的非public的類
-dontskipnonpubliclibraryclassmembers # 指定不去忽略包可見的庫類的成員
-dontoptimize # 表示不進行優化,建議使用此選項,因為根據proguard-android-optimize.txt中的描述,優化可能會造成一些潛在風險,不能保證在所有版本的Dalvik上都正常運行
-dontpreverify # 表示不進行預校驗,這個預校驗是作用在Java平台上的,Android平台上不需要這項功能,去掉之後還可以加快混淆速度
-verbose # 表示打印混淆的詳細信息
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/* # 混淆時所采用的算法
-dump dump.txt # 描述apk內所有class文件的內部結構
-printseeds seeds.txt # 列出了沒有被混淆的類和成員
-printusage unused.txt # 列出了源代碼中被刪除在apk中不存在的代碼
-printmapping mapping.txt # 表示混淆前後代碼的對照表
3.2、公共組件
-keep public class * extends android.app.Activity # 保留繼承自Activity類不被混淆
-keep public class * extends android.app.Application # 保留繼承自Application類不被混淆
-keep public class * extends android.support.multidex.MultiDexApplication # 保留繼承自MultiDexApplication類不被混淆
-keep public class * extends android.app.Service # 保留繼承自Service類不被混淆
-keep public class * extends android.content.BroadcastReceiver # 保留繼承自BroadcastReceiver類不被混淆
-keep public class * extends android.content.ContentProvider # 保留繼承自ContentProvider類不被混淆
-keep public class * extends android.app.backup.BackupAgentHelper # 保留繼承自BackupAgentHelper類不被混淆
-keep public class * extends android.preference.Preference # 保留繼承自Preference類不被混淆
-keep public class com.google.vending.licensing.ILicensingService # 保留Google包下ILicensingService類不被混淆
-keep public class com.android.vending.licensing.ILicensingService # 保留Android包下ILicensingService類不被混淆
-keepattributes *Annotation*,InnerClasses,Signature,SourceFile,LineNumberTable # 保留相關屬性
-keepclasseswithmembernames class * { # 保持native方法不被混淆
native ;
}
-keepclassmembers 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);
}
-keepclasseswithmembers class * { # 保持自定義控件類不被混淆
public (android.content.Context, android.util.AttributeSet);
public (android.content.Context, android.util.AttributeSet, int);
}
-keepclassmembers class * extends android.app.Activity { # 表示不混淆Activity中參數是View的方法,主要針對在xml中配置onClick事件
public void *(android.view.View);
}
-keepclassmembers enum * { # 保持枚舉類不被混淆
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keep class * implements android.os.Parcelable { # 保持Parcelable不被混淆
public static final android.os.Parcelable$Creator *;
}
-keepclassmembers class * implements java.io.Serializable { # 保持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();
}
-keepclassmembers class **.R$* { # 表示不混淆R文件下的靜態字段
public static ;
}
-keepclassmembers class * extends android.webkit.webViewClient { # 保留WebView
public void *(android.webkit.webView, jav.lang.String);
public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap);
public boolean *(android.webkit.WebView, java.lang.String);
}
3.3、第三方庫
#EventBus
-keepattributes *Annotation*
-keepclassmembers class ** {
@org.greenrobot.eventbus.Subscribe ;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
# Only required if you use AsyncExecutor
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
(java.lang.Throwable);
}
-keepclassmembers class * { # 保持EventBus中Event事件接收
void *(**On*Event);
}
#注:此處以EventBus為例,描述第三方庫的保留方式,更多的第三方庫保留方式已提交到github上。
3.4、實體類
-keep class 你的實體類包名.** { *; }
3.5、反射相關
-keep class 你的類所在的包.** { *; }
3.6、JS調用相關
-keepattributes *JavascriptInterface*
-keep class **.Webview2JsInterface { *; } # 保持WebView對HTML頁面的API不被混淆
-keepclassmembers class fqcn.of.javascript.interface.for.webview { # 保留WebView
public *;
}
-keep class 你的類所在的包.** { *; }
#如果是內部類則使用如下方式
-keepclasseswithmembers class 你的類所在的包.父類$子類 { ; }
到此混淆相關介紹就已完成,完整的混淆文件已提交到github上,路徑如下:https://github.com/xiaoyaoyou1212/Note/blob/master/app/proguard-rules.pro.其中包含了大部分第三方庫的混淆方式。
注:混淆後生成的相關信息文件在當前module的build/outputs/mapping下,目錄下可能會有渠道名,下面再分為debug和release,與你的配置相關,其中的mapping.txt文件需要重點關注,該文件表示混淆前後代碼的對照表,這個文件非常重要,如果你的代碼混淆後會產生bug的話,log提示中是混淆後的代碼,希望定位到源代碼的話就可以根據mapping.txt反推,每次發布都要保留它方便該版本出現問題時調出日志進行排查,它可以根據版本號或是發布時間命名來保存或是放進代碼版本控制中。
Android應用是用Java編寫的,利用Android SDK編譯代碼,並且把所有的數據和資源文件打包成一個APK (Android Package)文件,這是一個後綴名為.apk的壓縮文件,APK文件中包含了一個Android應用程序的所有內容,是Android平台用於安裝應用程序的文件。APK就是一個zip壓縮包,解開這個APK包我們可以看到以下的結構:
從以上目錄結構中可以看出,如果需要縮小apk的大小,主要針對的是assets目錄、lib目錄、res目錄及classes.dex文件。其中的assets目錄主要是靜態加載到apk中的文件,這個可以根據是否需要來進行手動管理,沒什麼優化的空間,下文主要針對classes.dex文件、res目錄及lib目錄來進行優化講解。
2.1、Proguard混淆優化代碼
Proguard是一個很強悍的工具,它可以幫你在代碼編譯時對代碼進行混淆,優化和壓縮。它有一個專門用來減少apk文件大小的功能叫做tree-shaking。Proguard 會遍歷你的所有代碼然後找出無用處的代碼。所有這些不可達(或者不需要)的代碼都會在生成最終的apk文件之前被清除掉。Proguard 也會重命名你的類屬性,類和接口,然整個代碼盡可能地保持輕量級水平。
2.2、Lint檢查優化資源
混淆只能優化java代碼,不能對無用資源進行清理,而Lint則可以檢查所有無用的資源文件,只要使用命令./gradlew lint
或者在Android Studio工程中點擊Analyze->Inspect Code,選擇Whole Project點擊ok就行。它在檢測完之後會提供一份詳細的資源文件清單,並將無用的資源列在“UnusedResources: Unused resources” 區域之下。只要你不通過反射來反問這些無用資源,你就可以放心地移除這些文件了。
2.3、壓縮圖片
圖片資源的優化原則是:在不降低圖片效果、保證APK顯示效果的前提下縮小圖片文件的大小。
2.4、限制支持CPU架構
一般說來Android使用Java代碼即可以滿足大部分需求,不過還是有一小部分案例需要使用一些native code。CPU的架構主要分為以下幾種:ARM架構,ARM-V7架構,MIPS架構和X86架構,目前市場上使用的移動終端大多是基於ARM或者ARM-V7a架構的,X86和MIPS架構的移動智能終端比較少,所以有些應用程序lib目錄下只包含armeabi目錄或者armeabi-v7a目錄,也就是說lib目錄要根據CPU的架構來選,而市面上ARM架構的手機占大多數,所以一般的APK只包含ARM和ARM-V7a的so。
2.5、其他優化技巧
對資源文件進行取捨
defaultConfig {
resConfigs "en", "de", "fr", "it"
resConfigs "nodpi", "hdpi", "xhdpi", "xxhdpi", "xxxhdpi"
}
盡可能地重用鑒於谷歌最新推出的Android Studio備受開發者的推崇,所以也跟著體驗一下。一、介紹Android Studio Android Studio 是一個A
QQ厘米秀是騰訊手機QQ推出的全新功能玩法,厘米秀添加了讓人眼前一亮的人物動作互動功能,用戶可以通過手機QQ厘米秀的窗口與好友互動。還有一個故事卡線索功能,
ReactNative 讓開發者使用 JavaScript 和 React 編寫應用,利用相同的核心代碼就可以創建 基於Web,iOS 和 Android 平台的原生應用
能夠實現自定義UI是android開發很重要的一個階段,下面就和大家一起學習、分享。首先官網上有兩篇文章: 1、Custom Components 2、Cre