一. 優(yōu)化思路
- 現(xiàn)代編譯器非常智能, 對于
death blocks, 編譯器會(huì)直接刪除. 什么叫death blocks, 請看下面的代碼:
public void method() {
if(false) {
System.out.println("Read the fucking source code !");
}
}
此方法中的if語句塊, 就是一個(gè)death blocks, 它是永遠(yuǎn)都不會(huì)執(zhí)行的. 因此編譯器會(huì)直接將其刪除, 刪除后代碼如下:
public void method() {
}
編譯器優(yōu)化也是有條件的, 即if括號(hào)中的值必須在編譯是就能確定為false且不可更改. 如下面的程序中, if括號(hào)中的值在運(yùn)行時(shí)可以隨時(shí)改變, flag的值可以為true也可以為false, 它是不確定的. 就算初始值為false, 但是因?yàn)樗强勺兊? 因此編譯器不能優(yōu)化掉if語句塊.
public class Demo {
boolean flag = false;
public void d(String tag, String msg) {
if(flag) {
Log.d(tag, msg);
}
}
}
再看下面一個(gè)程序:
public final class DevUtil {
//Bad implementation, dependence app module (MyApplication)
private static final boolean flag = debuggable(MyApplication.getInstance());
private DevUtil() {
//no instance
}
//什么事情都不做, 只是為了初始化flag的值
public static void init() {
}
public static void d(String tag, String msg) {
if(flag) {
Log.d(tag, msg);
}
}
private static boolean debuggable(Context appContext) {
final int DEBUG_SIGNATURE_HASH = -545093291;
final int ONLINE_SIGNATURE_HASH = -283702024;
// 判斷是否為調(diào)試狀態(tài)
// http://stackoverflow.com/questions/3029819/android-automatically-choose-debug-release-maps-api-key
PackageManager manager = appContext.getPackageManager();
try {
PackageInfo info = manager.getPackageInfo(appContext.getPackageName(), PackageManager.GET_SIGNATURES);
for (Signature sig : info.signatures) {
int sigHashCode = sig.hashCode();
switch(sigHashCode) {
case DEBUG_SIGNATURE_HASH:
return true;
case ONLINE_SIGNATURE_HASH:
return false;
}
}
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return false;
}
}
雖然DevUtil類中l(wèi)og輸出的開關(guān)邊是常量(不可變的), 但是由于賦值給flag的是一個(gè)方法調(diào)用, 編譯時(shí)并不能確定其值為false, 因此if語句塊也是不能優(yōu)化掉的.
要優(yōu)化掉一個(gè)if語句塊, 必須同時(shí)滿足兩個(gè)條件:
- if括號(hào)中的值必須編譯時(shí)就能確認(rèn)為false
- 且if語句中的值是不可變的
要滿足上面的條件, if語句只中的值只能是字面常量false, 或者是一個(gè)常量 --- 賦值給這個(gè)常量的必須是一個(gè)字面常量或者一個(gè)字面常量的引用(指向一個(gè)字面常量的常量, 如final boolean flag = false; flag就是字面常量的引用)
- Android Studio編譯module之后會(huì)在
<module>/build/generated/source/buildConfig/{flavor}/{buildType}目錄下生成一個(gè)BuildConfig.java文件. 如果沒有定義productFlavors生成的BuildConfig位于<module>/build/generated/source/buildConfig/{buildType}目錄下. 如圖:

BuildConfig.java文件(buildType為release)的內(nèi)容如下:
/**
* Automatically generated file. DO NOT MODIFY
*/
package com.stone.mvp_demo;
public final class BuildConfig {
public static final boolean DEBUG = false;
public static final String APPLICATION_ID = "com.stone.mvp_demo";
public static final String BUILD_TYPE = "release";
public static final String FLAVOR = "flavor1";
public static final int VERSION_CODE = 1;
public static final String VERSION_NAME = "1.0";
// Fields from build type: release
public static final boolean LOG_DEBUG = false;
}
BuildConfig的DEBUG字段就是一個(gè)字面常量的引用. 這是滿足編譯器優(yōu)化條件的.
BuildConfig類的DEBUG字段會(huì)因buildType的不同而不同, 具體為debug版DEBUG的值為true, release版DEBUG的值為false. 因此可以利用BuildConfig類的DEBUG字段來作為log的開關(guān). 當(dāng)我們打release包時(shí), buildType肯定是release, 因此DEBUG的值是false. 此時(shí)log輸出代碼塊變成了Death Blocks, 形如:
if(false) {
Log.e(TAG, message)
}
編譯器會(huì)刪除此段. 這樣log輸出被優(yōu)化成一個(gè)空方法, 減少了方法內(nèi)部語句的入棧出棧.
二. 具體代碼
推薦把DevUtil放在公共庫中(這里有個(gè)巨坑, 請參考《Android中使用BuildConfig.DEBUG必須知道的內(nèi)幕》), 方便代碼重用.
public final class DevUtil {
private static final boolean isDebug = BuildConfig.DEBUG;
private DevUtil() {
//no instance
}
public static void d(String tag, String msg) {
if (isDebug) {
Log.d(tag, msg + " - tag:" + tag);
}
}
public static void v(String tag, String msg) {
if (isDebug) {
Log.v(tag, msg + " - tag:" + tag);
}
}
public static void w(String tag, String msg, Throwable e) {
if (isDebug) {
Log.w(tag, msg + " - tag:" + tag, e);
}
}
// 更多l(xiāng)og輸出方法 ....
public static boolean isDebug() {
return isDebug;
}
}
反編譯后的DevUtil類變成了下面的樣子:

三. proguard優(yōu)化配置
推薦release版的混淆開啟優(yōu)化選項(xiàng), 要使用開啟優(yōu)化選項(xiàng)的規(guī)則文件, 默認(rèn)配置必須使用<ANDROID_SDK>/tools/proguard/proguard-android-optimize.txt, 而不是<ANDROID_SDK>/tools/proguard/proguard-android.txt文件. proguard-android.txt文件中已經(jīng)把優(yōu)化選項(xiàng)給關(guān)閉了, 并且告訴你僅僅包含優(yōu)化配置選項(xiàng)是沒有用的, 因?yàn)檫@個(gè)文件已經(jīng)將優(yōu)化的開關(guān)給關(guān)了, 你需要使用proguard-android-optimize.txt這個(gè)文件來開啟混淆的優(yōu)化功能, proguard-android.txt文件的部分截圖如下:

主項(xiàng)目module下的構(gòu)建腳本(build.gradle)的buildType配置(release版配置)如下:

proguard優(yōu)化開啟后, 沒有用到的類、方法、變量、無用語句塊 ... 等會(huì)被移除掉, 這樣程序會(huì)有一定的性能提升.
下面推薦幾款反編譯工具:
(1) 農(nóng)民工專用:
apktool + jd-gui + dex2jar--- 免費(fèi)
apktool網(wǎng)站: https://ibotpeaches.github.io/Apktool/
apktool下載地址: https://bitbucket.org/iBotPeaches/apktool/downloads
jd-gui官網(wǎng): http://jd.benow.ca/
jd-gui GitHub地址: https://github.com/java-decompiler/jd-gui
dex2jar下載地址: https://sourceforge.net/projects/dex2jar/
dex2jar GitHub地址: https://github.com/pxb1988/dex2jar(2) 小市民專用: jadx --- 免費(fèi) 墻裂推薦(?。?^)
(3) 土豪專用: JEB --- 收費(fèi)