天天看點

Android Studio 3.0+ 新Dex編譯器D8 Desugar R8〇.序一.D8二.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檔案将擁有相同或者是更好的運作時性能。

Android Studio 3.0+ 新Dex編譯器D8 Desugar R8〇.序一.D8二.R8參考資料
Android Studio 3.0+ 新Dex編譯器D8 Desugar R8〇.序一.D8二.R8參考資料

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 支援。

Android Studio 3.0+ 新Dex編譯器D8 Desugar R8〇.序一.D8二.R8參考資料

目前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 = false

Android Studio 3.0+ 新Dex編譯器D8 Desugar R8〇.序一.D8二.R8參考資料
Android Studio 3.0+ 新Dex編譯器D8 Desugar R8〇.序一.D8二.R8參考資料

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);
    //lambda寫法
    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)/

Android Studio 3.0+ 新Dex編譯器D8 Desugar R8〇.序一.D8二.R8參考資料
public class MainActivity extends Activity {

  public MainActivity() {
  }

  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    this.setContentView();
    TextView tv = (TextView)this.findViewById();
    //直接使用Lambda表達式脫糖後的靜态引用
    tv.setOnClickListener(MainActivity$$Lambda$$instance);
    tv.setOnClickListener(new OnClickListener() {
      public void onClick(View view) {
        Log.d("MainActivity", "MainActivity");
      }
    });
  }
}
           
//Class檔案中生成靜态引用
final class MainActivity$$Lambda$0 implements OnClickListener {

  static final OnClickListener $instance = new MainActivity$$Lambda$();

  private MainActivity$$Lambda$() {
  }

  public void onClick(View var1) {
    MainActivity.lambda$onCreate$$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();
    TextView tv = (TextView)this.findViewById();
    //注意this傳遞過去了。類似于内部類的寫法
    tv.setOnClickListener(new MainActivity$$Lambda$(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$;

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

  public void onClick(View var1) {
    this.arg$lambda$onCreate$$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表達式生成一個靜态對象。

Android Studio 3.0+ 新Dex編譯器D8 Desugar R8〇.序一.D8二.R8參考資料
Android Studio 3.0+ 新Dex編譯器D8 Desugar R8〇.序一.D8二.R8參考資料

當然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

參考資料

  • Android Studio switching to D8 dexer
  • Next-generation Dex Compiler Now in Preview
  • 官方文檔 : 使用 Java 8 語言功能