Android Studio 3.0+ 新Dex編譯器D8 Desugar R8

〇.序

將.class自己碼轉化為.dex字節碼作為Apk打包的關鍵步驟,Google打算在Android 3.0中引入D8作為原先Dex的升級版,以及R8作為原本Proguard 壓縮與優化(minification、shrinking、optimization)部分的替代品。升級Dex編譯器將直接影響構建時間,.dex文件大小,運行時性能。

一.D8

1.1 D8 的功能是把java字節碼轉化成dex代碼,D8作為DX的一個替換方案。

谷歌通過自己的 基準測試項目測出,編譯時間縮短了20%,而且.dex文件更小,雖然只有幾個百分比。D8編譯的.dex文件將擁有相同或者是更好的運行時性能。

Dex編譯時間 DX VS D8
dex文件大小 DX VS D8

Java 8支持相關

Android Studio 3.0 及以上版本支持所有 Java 7 語言功能,以及部分 Java 8 語言功能(具體因平臺版本而異)。
注:在開發 Android 應用時,可以選擇使用 Java 8 語言功能。 您可以將項目的源代碼和目標代碼兼容性值保留為 Java 7,但仍須使用 JDK 8 進行編譯。
Android Studio 為使用部分 Java 8 語言功能及利用這些功能的第三方庫提供內置支持。 如圖 1 所示,默認工具鏈對 javac 編譯器的輸出執行字節碼轉換(稱為 desugar),從而實現新語言功能。 Jack 不再受支持,您需要首先停用 Jack 才能使用默認工具鏈內置的 Java 8 支持。

采用 desugar 字節碼轉換的 Java 8 語言功能支持。

目前Java 8語言支持的處理是在javac之后,與字節碼處理工具處理之前。在接下來的幾個月,這個步驟將會被移動到pipeline的后一個階段,作為D8的一部分。

其帶來的影響:

  • 減少這塊的編譯時間
  • 可以優化更多代碼
  • 這么一來,所有字節碼處理工具就必須要支持Java8的字節碼格式了。

1.2 D8的使用

已經在Android Studio 3.0 Beta release中引入

  • Android Studio 3.0
    需要主動在gradle.properties文件中新增:android.enableD8=true
  • Android Studio 3.1或之后的版本
    在3.1或之后的版本D8將會被作為默認的Dex編譯器。如果遇到問題,你可以通過修改gradle.properties文件里的一個屬性恢復到DX android.enableD8=false
  • 除了其他好處外,使用D8還有一個好處,就是支持 脫糖,讓Java 8才提供的特性(如lambdas)可以轉換成Java 7特性。把脫糖步驟集成進D8影響了所有讀或寫.class字節碼的開發工具,因為它會使用Java 8格式。你可以在gradle文件中設置一個屬性,恢復到以前的行為,讓脫糖發生在Java編譯之后,.class字節碼仍遵循Java 7格式:android.enableD8.desugaring = true
D8 dexer

d8 dexer desuger

1.3脫糖(Desugar)

當我們選擇JDK8以上版本時,有時候會使用lambda表達式,在設置android.enableD8.desugaring = false的時候。編譯鏈會對lambda表達式進行一次脫糖處理。請看下面的例子。

1.3.1 純函數脫糖

源代碼很簡單:

一個簡單的Activity,設置ClickListener一種是Java7以下的傳統寫法,一種是Java8的Lambda表達式寫法

public class MainActivity extends Activity {

  @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    TextView tv = findViewById(R.id.click);
    tv.setOnClickListener(view -> {
      Log.d("MainActivity", "MainActivity");
    });
    tv.setOnClickListener(new View.OnClickListener() {
      @Override public void onClick(View view) {
        Log.d("MainActivity", "MainActivity");
      }
    });

  }
}

編譯后的Class文件如下:

路徑為
app/build/intermediates/transforms/desugar/release(buildType)/0/com.jamin.d8desugar(packageName)/

desugar生成的class放置的位置

public class MainActivity extends Activity {

  public MainActivity() {
  }

  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    this.setContentView(2130968576);
    TextView tv = (TextView)this.findViewById(2130903040);
    tv.setOnClickListener(MainActivity$$Lambda$0.$instance);
    tv.setOnClickListener(new OnClickListener() {
      public void onClick(View view) {
        Log.d("MainActivity", "MainActivity");
      }
    });
  }
}
final class MainActivity$$Lambda$0 implements OnClickListener {
  static final OnClickListener $instance = new MainActivity$$Lambda$0();

  private MainActivity$$Lambda$0() {
  }

  public void onClick(View var1) {
    MainActivity.lambda$onCreate$0$MainActivity(var1);
  }
}

實際上非D8脫糖,為了保證JAVA7及以下的兼容性。是將lambda表達式的在javac編譯class的時候就已經將lambda表達式轉化成更高兼容度的低版本代碼。好處是在編譯鏈中,有時候會使用一些java7的工具。他們對于java8的語法糖是無法識別的。

1.3.2 非純函數脫糖

好,我們簡單改寫一下源文件

public class MainActivity extends Activity {

  String abc = "abc";

  @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    TextView tv = findViewById(R.id.click);
    tv.setOnClickListener(view -> {
      Log.d("MainActivity", "MainActivity" + abc);
    });
    tv.setOnClickListener(new View.OnClickListener() {
      @Override public void onClick(View view) {
        Log.d("MainActivity", "MainActivity" + abc);
      }
    });
  }
}
生成的class文件如下:
public class MainActivity extends Activity {
  String abc = "abc";

  public MainActivity() {
  }

  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    this.setContentView(2130968576);
    TextView tv = (TextView)this.findViewById(2130903040);
    //注意this傳遞過去了。類似于內部類的寫法
    tv.setOnClickListener(new MainActivity$$Lambda$0(this));
    tv.setOnClickListener(new OnClickListener() {
      public void onClick(View view) {
        Log.d("MainActivity", "MainActivity" + MainActivity.this.abc);
      }
    });
  }
}
// $FF: synthetic class
final class MainActivity$$Lambda$0 implements OnClickListener {
  private final MainActivity arg$1;

  MainActivity$$Lambda$0(MainActivity var1) {
    this.arg$1 = var1;
  }

  public void onClick(View var1) {
    this.arg$1.lambda$onCreate$0$MainActivity(var1);
  }
}

此時生成的class文件就不是純函數了。所以會不會內存泄漏?

1.3.3 D8脫糖

在設置android.enableD8.desugaring = true的時候(高版本,比如AGP的版本是com.android.tools.build:gradle:3.3.0-alpha03時,默認是D8脫糖),D8脫糖就不會在transforms目錄下生成desugar目錄。反編譯transforms/dexBuilder/中的jar包。可以看到在jar包中,已經是脫糖后的結果了。大家可以看下圖。也是把lambda表達式生成一個靜態對象。

MainActivity

Lambda

當然D8脫糖,要求編譯鏈中所有工具都支持java8,不然不認識class文件中的部分語法糖。D8脫糖的好處是什么呢。官方的話說就是可以提高編譯速度。

二.R8

R8作為原本Proguard 壓縮與優化(minification、shrinking、optimization)部分的替代品,依然使用與Proguard一樣的keep規則。
目前R8已經開源: r8/r8,其包含了D8與R8。

目前R8還沒有整合進Android Gradle plugin,不過由于其已經開源,根據文檔可以很快的在python環境下運行起來:

  1. 確保本地已經安裝了python 2.7或更高版本(macOS Sierra自帶python 2.7)。
  2. 由于R8項目使用chromium項目提供的depot_tools管理依賴,因此先安裝depot_tools
  3. Clone R8項目:git clone https://r8.googlesource.com/r8 && cd r8
  4. 下載一個Gradle版去編譯,并且聲稱兩個jar文件: build/libs/d8.jar與build/libs/r8.jar: python tools/gradle.py d8 r8
    根據r8文檔進行使用即可

BREAKING NEWS:新版AndroidStudio可以體驗一下。

New code shrinker R8 is a new tool for code shrinking and obfuscation that replaces ProGuard. You can start using the preview version of R8 by including the following in your project’s gradle.properties file: android.enableR8 = true
官方文檔: New code shrinker

參考資料

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,967評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,273評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,870評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,742評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,527評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,010評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,108評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,250評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,769評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,656評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,853評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,371評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,103評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,472評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,717評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,487評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,815評論 2 372

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,611評論 25 708
  • Android 新一代編譯 toolchain Jack & Jill 簡介 2016 年 3 月 10 日, G...
    heiheiwanne閱讀 1,220評論 0 3
  • 我叫cimi,是人類社會為了區別我們,給予物質已命名已概念,對于我那個符號是:cimi;橘貓;豆芽的情人受;大力...
    feono閱讀 608評論 1 6
  • 時光如梭塵路不平,大多數人的每時每刻都在為生存而忙忙碌碌的奔波著,為生活而辛辛苦苦的操持著。 盡管在現實狀況里很多...
    大浪淘沙S6閱讀 316評論 0 1
  • “你買這么大的東西之前考慮過三輪車的感受嗎?”---今天一早我被快遞小哥在電話那頭這樣問到。 “那你送不了貨的話,...
    雜食的明子閱讀 615評論 0 0