ART 服務配置

在開始之前,請參閱ART 服務的高級概述。

從 Android 14 開始,應用程式的裝置上 AOT 編譯(又稱 dexopt)由 ART 服務處理。 ART服務是ART模組的一部分,您可以透過系統屬性和API對其進行自訂。

系統屬性

ART 服務支援所有相關的dex2oat 選項

此外,ART 服務支援以下系統屬性:

pm.dexopt.<原因>

這是一組系統屬性,用於確定Dexopt 場景中所述的所有預定義編譯原因的預設編譯器過濾器。

有關詳細信息,請參閱編譯器過濾器

標準預設值為:

pm.dexopt.first-boot=verify
pm.dexopt.boot-after-ota=verify
pm.dexopt.boot-after-mainline-update=verify
pm.dexopt.bg-dexopt=speed-profile
pm.dexopt.inactive=verify
pm.dexopt.cmdline=verify

pm.dexopt.shared(預設:速度)

這是其他應用程式使用的應用程式的後備編譯器過濾器。

原則上,ART 服務會在可能的情況下對所有應用程式進行設定檔引導編譯 ( speed-profile ),通常是在背景 dexopt 期間。但是,有一些應用程式被其他應用程式使用(透過<uses-library>或使用Context#createPackageContextCONTEXT_INCLUDE_CODE動態載入)。由於隱私原因,此類應用程式無法使用本機設定檔。

對於此類應用程序,如果請求設定檔引導編譯,ART 服務首先嘗試使用雲端設定檔。如果雲端設定檔不存在,ART 服務將回退使用pm.dexopt.shared指定的編譯器過濾器。

如果請求的編譯不是設定檔引導的,則此屬性無效。

pm.dexopt.<原因>.並發(預設值:1)

這是由於某些預定義編譯原因( first-bootboot-after-otaboot-after-mainline-updatebg-dexopt )而呼叫的dex2oat數量。

請注意,此選項的效果與dex2oat 資源使用選項dalvik.vm.*dex2oat-threadsdalvik.vm.*dex2oat-cpu-set和任務設定檔)結合:

  • dalvik.vm.*dex2oat-threads控制每個 dex2oat 呼叫的線程數,而pm.dexopt.<reason>.concurrency控制 dex2oat 呼叫的數量。也就是說,最大並發線程數是兩個系統屬性的乘積。
  • dalvik.vm.*dex2oat-cpu-set和任務設定檔始終限制 CPU 核心使用情況,無論並發執行緒的最大數量(如上所述)為何。

無論dalvik.vm.*dex2oat-threads是多少,單一 dex2oat 呼叫可能無法充分利用所有 CPU 核心。因此,增加 dex2oat 呼叫次數( pm.dexopt.<reason>.concurrency )可以更好地利用 CPU 內核,從而加快 dexopt 的整體進度。這在引導期間特別有用。

但是,過多的 dex2oat 呼叫可能會導致裝置記憶體不足,儘管可以透過將dalvik.vm.dex2oat-swap設為true以允許使用交換檔案來緩解這種情況。過多的呼叫也可能導致不必要的上下文切換。因此,應根據具體產品仔細調整該數字。

pm.dexopt.downgrade_after_inactive_days(預設:未設定)

如果設定此選項,ART 服務僅刪除最近給定天數內使用的應用程式。

此外,如果儲存空間接近較低,則在背景 dexopt 期間,ART 服務會降級最近給定天數內未使用的應用程式的編譯器過濾器,以釋放空間。編譯器原因是inactive ,編譯器過濾器由pm.dexopt.inactive決定。觸發此功能的空間閾值是儲存管理器的低空間閾值(可透過全域設定sys_storage_threshold_percentagesys_storage_threshold_max_bytes進行配置,預設值:500MB)加上 500MB。

如果您透過ArtManagerLocal#setBatchDexoptStartCallback自訂套件列表,則BatchDexoptStartCallbackbg-dexopt提供的清單中的套件永遠不會降級。

pm.dexopt.disable_bg_dexopt(預設值:false)

這僅用於測試。它阻止 ART 服務調度後台 dexopt 作業。

如果後台 dexopt 作業已排程但尚未執行,則此選項無效。也就是說,作業仍然會運行。

防止後台 dexopt 作業運行的建議命令序列是:

setprop pm.dexopt.disable_bg_dexopt true
pm bg-dexopt-job --disable

第一行阻止後台 dexopt 作業被調度(如果尚未調度)。第二行取消後台 dexopt 作業(如果已計劃)的計劃,並立即取消後台 dexopt 作業(如果正在運行)。

ART 服務 API

ART 服務公開 Java API 以進行客製化。 API 在ArtManagerLocal中定義。有關用法,請參閱art/libartservice/service/java/com/android/server/art/ArtManagerLocal.java中的 Javadoc( Android 14 原始程式碼未發布的開發原始程式碼)。

ArtManagerLocalLocalManagerRegistry持有的單例。輔助函數com.android.server.pm.DexOptHelper#getArtManagerLocal可協助您取得它。

import static com.android.server.pm.DexOptHelper.getArtManagerLocal;

大多數API都需要一個PackageManagerLocal.FilteredSnapshot實例,它保存所有應用程式的資訊。您可以透過呼叫PackageManagerLocal#withFilteredSnapshot來取得它,其中PackageManagerLocal也是LocalManagerRegistry持有的單例,可以從com.android.server.pm.PackageManagerServiceUtils#getPackageManagerLocal取得。

import static com.android.server.pm.PackageManagerServiceUtils.getPackageManagerLocal;

以下是 API 的一些典型用例。

觸發應用程式的 dexopt

您可以透過呼叫ArtManagerLocal#dexoptPackage隨時觸發任何應用程式的 dexopt。

try (var snapshot = getPackageManagerLocal().withFilteredSnapshot()) {
  getArtManagerLocal().dexoptPackage(
      snapshot,
      "com.google.android.calculator",
      new DexoptParams.Builder(ReasonMapping.REASON_INSTALL).build());
}

您也可以傳遞您自己的 dexopt 原因。如果這樣做,則必須明確設定優先權類別和編譯器篩選器。

try (var snapshot = getPackageManagerLocal().withFilteredSnapshot()) {
  getArtManagerLocal().dexoptPackage(
      snapshot,
      "com.google.android.calculator",
      new DexoptParams.Builder("my-reason")
          .setCompilerFilter("speed-profile")
          .setPriorityClass(ArtFlags.PRIORITY_BACKGROUND)
          .build());
}

取消 dexopt

如果某個操作是由dexoptPackage呼叫啟動的,您可以傳遞一個取消訊號,該訊號允許您在某個時刻取消該操作。當您非同步運行 dexopt 時,這可能很有用。

Executor executor = ...;  // Your asynchronous executor here.
var cancellationSignal = new CancellationSignal();
executor.execute(() -> {
  try (var snapshot = getPackageManagerLocal().withFilteredSnapshot()) {
    getArtManagerLocal().dexoptPackage(
        snapshot,
        "com.google.android.calculator",
        new DexoptParams.Builder(ReasonMapping.REASON_INSTALL).build(),
        cancellationSignal);
  }
});

// When you want to cancel the operation.
cancellationSignal.cancel();

您也可以取消由 ART 服務啟動的後台 dexopt。

getArtManagerLocal().cancelBackgroundDexoptJob();

取得 dexopt 結果

如果操作是由dexoptPackage呼叫發起的,您可以從傳回值中取得結果。

DexoptResult result;
try (var snapshot = getPackageManagerLocal().withFilteredSnapshot()) {
  result = getArtManagerLocal().dexoptPackage(...);
}

// Process the result here.
...

在許多場景下,ART Service 也會自行啟動 dexopt 操作,例如後台 dexopt。若要偵聽所有 dexopt 結果,無論操作是由dexoptPackage呼叫還是由 ART 服務啟動,請使用ArtManagerLocal#addDexoptDoneCallback

getArtManagerLocal().addDexoptDoneCallback(
    false /* onlyIncludeUpdates */,
    Runnable::run,
    (result) -> {
      // Process the result here.
      ...
    });

第一個參數決定是否僅在結果中包含更新。如果您只想監聽 dexopt 更新的套件,請將其設為 true。

第二個參數是回呼的執行者。若要在執行 dexopt 的相同執行緒上執行回調,請使用Runnable::run 。如果您不希望回調阻止 dexopt,請使用非同步執行器。

您可以新增多個回調,ART Service 將依序執行所有回呼。所有回調將在以後的所有呼叫中保持活動狀態,除非您將其刪除。

如果要刪除回調,請在新增回呼時保留回呼的引用,並使用ArtManagerLocal#removeDexoptDoneCallback

DexoptDoneCallback callback = (result) -> {
  // Process the result here.
  ...
};

getArtManagerLocal().addDexoptDoneCallback(
    false /* onlyIncludeUpdates */, Runnable::run, callback);

// When you want to remove it.
getArtManagerLocal().removeDexoptDoneCallback(callback);

自訂套件清單和dexopt參數

ART 服務在啟動和背景 dexopt 期間自行啟動 dexopt 操作。若要自訂這些操作的套件清單或 dexopt 參數,請使用ArtManagerLocal#setBatchDexoptStartCallback

getArtManagerLocal().setBatchDexoptStartCallback(
    Runnable::run,
    (snapshot, reason, defaultPackages, builder, cancellationSignal) -> {
      switch (reason) {
        case ReasonMapping.REASON_BG_DEXOPT:
          var myPackages = new ArrayList<String>(defaultPackages);
          myPackages.add(...);
          myPackages.remove(...);
          myPackages.sort(...);
          builder.setPackages(myPackages);
          break;
        default:
          // Ignore unknown reasons.
      }
    });

您可以將項目新增至套件清單、從中刪除項目、對其排序,甚至使用完全不同的清單。

您的回呼必須忽略未知原因,因為將來可能會添加更多原因。

您最多可以設定一個BatchDexoptStartCallback 。除非您清除它,否則回呼將對所有未來的呼叫保持活動狀態。

如果您想要清除回調,請使用ArtManagerLocal#clearBatchDexoptStartCallback

getArtManagerLocal().clearBatchDexoptStartCallback();

自訂後台dexopt作業的參數

預設情況下,當裝置空閒和充電時,後台 dexopt 作業每天運行一次。這可以使用ArtManagerLocal#setScheduleBackgroundDexoptJobCallback進行更改。

getArtManagerLocal().setScheduleBackgroundDexoptJobCallback(
    Runnable::run,
    builder -> {
      builder.setPeriodic(TimeUnit.DAYS.toMillis(2));
    });

您最多可以設定一個ScheduleBackgroundDexoptJobCallback 。除非您清除它,否則回呼將對所有未來的呼叫保持活動狀態。

如果您想要清除回調,請使用ArtManagerLocal#clearScheduleBackgroundDexoptJobCallback

getArtManagerLocal().clearScheduleBackgroundDexoptJobCallback();

暫時禁用 dexopt

由 ART 服務啟動的任何 dexopt 操作都會觸發BatchDexoptStartCallback 。您可以繼續取消操作來有效停用dexopt。

如果您取消的操作是後台 dexopt,它將遵循預設重試策略(30 秒,指數,上限為 5 小時)。

// Good example.

var shouldDisableDexopt = new AtomicBoolean(false);

getArtManagerLocal().setBatchDexoptStartCallback(
    Runnable::run,
    (snapshot, reason, defaultPackages, builder, cancellationSignal) -> {
      if (shouldDisableDexopt.get()) {
        cancellationSignal.cancel();
      }
    });

// Disable dexopt.
shouldDisableDexopt.set(true);
getArtManagerLocal().cancelBackgroundDexoptJob();

// Re-enable dexopt.
shouldDisableDexopt.set(false);

您最多可以有一個BatchDexoptStartCallback 。如果您還想使用BatchDexoptStartCallback自訂套件清單或 dexopt 參數,則必須將程式碼合併到一個回呼中。

// Bad example.

// Disable dexopt.
getArtManagerLocal().unscheduleBackgroundDexoptJob();

// Re-enable dexopt.
getArtManagerLocal().scheduleBackgroundDexoptJob();

應用安裝時執行的 dexopt 操作不是由 ART 服務啟動的。相反,它是由套件管理器透過dexoptPackage呼叫啟動的。因此,它不會觸發BatchDexoptStartCallback 。若要在應用程式安裝時停用 dexopt,請阻止套件管理器呼叫dexoptPackage

覆蓋某些套件的編譯器過濾器(Android 15(AOSP 實驗)+)

您可以透過setAdjustCompilerFilterCallback註冊回呼來覆蓋某些套件的編譯器過濾器。每當要對套件進行 dexopt 時,都會呼叫該回調,無論 dexopt 是由 ART 服務在啟動期間和後台 dexopt 啟動還是由dexoptPackage API 呼叫啟動。

如果套件不需要調整,回呼必須回傳originalCompilerFilter

getArtManagerLocal().setAdjustCompilerFilterCallback(
    Runnable::run,
    (packageName, originalCompilerFilter, reason) -> {
      if (isVeryImportantPackage(packageName)) {
        return "speed-profile";
      }
      return originalCompilerFilter;
    });

您只能設定一個AdjustCompilerFilterCallback 。如果要使用AdjustCompilerFilterCallback覆蓋多個套件的編譯器過濾器,則必須將程式碼合併到一個回呼中。除非您清除它,否則回呼對於所有未來的呼叫都保持活動狀態。

如果您想要清除回調,請使用ArtManagerLocal#clearAdjustCompilerFilterCallback

getArtManagerLocal().clearAdjustCompilerFilterCallback();

其他客製化

ART 服務也支援一些其他自訂。

設定後台dexopt的熱閾值

後台 dexopt 作業的熱控制由 Job Scheduler 執行。當溫度達到THERMAL_STATUS_MODERATE時,作業將立即取消。 THERMAL_STATUS_MODERATE的閾值是可調的。

判斷後台dexopt是否正在運行

後台 dexopt 作業由 Job Scheduler 管理,其作業 ID 為27873780 。若要確定作業是否正在執行,請使用 Job Scheduler API。

// Good example.

var jobScheduler =
    Objects.requireNonNull(mContext.getSystemService(JobScheduler.class));
int reason = jobScheduler.getPendingJobReason(27873780);

if (reason == PENDING_JOB_REASON_EXECUTING) {
  // Do something when the job is running.
  ...
}
// Bad example.

var backgroundDexoptRunning = new AtomicBoolean(false);

getArtManagerLocal().setBatchDexoptStartCallback(
    Runnable::run,
    (snapshot, reason, defaultPackages, builder, cancellationSignal) -> {
      if (reason.equals(ReasonMapping.REASON_BG_DEXOPT)) {
        backgroundDexoptRunning.set(true);
      }
    });

getArtManagerLocal().addDexoptDoneCallback(
    false /* onlyIncludeUpdates */,
    Runnable::run,
    (result) -> {
      if (result.getReason().equals(ReasonMapping.REASON_BG_DEXOPT)) {
        backgroundDexoptRunning.set(false);
      }
    });

if (backgroundDexoptRunning.get()) {
  // Do something when the job is running.
  ...
}

提供 dexopt 的設定文件

若要使用設定檔來引導 dexopt,請將.prof檔或.dm檔放在 APK 旁邊。

.prof檔案必須是二進位格式的設定文件,且檔案名稱必須是 APK + .prof的檔案名稱。例如,

base.apk.prof

.dm檔案的檔案名稱必須是 APK 的檔案名,並將副檔名替換為.dm 。例如,

base.dm

若要驗證設定檔是否用於 dexopt,請使用speed-profile執行 dexopt 並檢查結果。

pm art clear-app-profiles <package-name>
pm compile -m speed-profile -f -v <package-name>

第一行清除執行階段產生的所有設定檔(即/data/misc/profiles中的設定檔)(如果有),以確保 APK 旁邊的設定檔是 ART 服務可能使用的唯一設定檔。第二行使用speed-profile執行 dexopt ,並傳遞-v來列印詳細結果。

如果正在使用設定文件,您會在結果中看到actualCompilerFilter=speed-profile 。否則,您會看到actualCompilerFilter=verify 。例如,

DexContainerFileDexoptResult{dexContainerFile=/data/app/~~QR0fTV0UbDbIP1Su7XzyPg==/com.google.android.gms-LvusF2uARKOtBbcaPHdUtQ==/base.apk, primaryAbi=true, abi=x86_64, actualCompilerFilter=speed-profile, status=PERFORMED, dex2oatWallTimeMillis=4549, dex2oatCpuTimeMillis=14550, sizeBytes=3715344, sizeBeforeBytes=3715344}

ART 服務不使用該設定檔的典型原因包括以下幾點:

  • 設定檔的檔案名稱錯誤或它不在 APK 旁邊。
  • 設定檔的格式錯誤。
  • 設定檔與 APK 不符。 (設定檔中的校驗和與 APK 中.dex檔的校驗和不符。)