在開始之前,請參閱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#createPackageContext
和CONTEXT_INCLUDE_CODE
動態載入)。由於隱私原因,此類應用程式無法使用本機設定檔。
對於此類應用程序,如果請求設定檔引導編譯,ART 服務首先嘗試使用雲端設定檔。如果雲端設定檔不存在,ART 服務將回退使用pm.dexopt.shared
指定的編譯器過濾器。
如果請求的編譯不是設定檔引導的,則此屬性無效。
pm.dexopt.<原因>.並發(預設值:1)
這是由於某些預定義編譯原因( first-boot
、 boot-after-ota
、 boot-after-mainline-update
和bg-dexopt
)而呼叫的dex2oat數量。
請注意,此選項的效果與dex2oat 資源使用選項( dalvik.vm.*dex2oat-threads
、 dalvik.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_percentage
和sys_storage_threshold_max_bytes
進行配置,預設值:500MB)加上 500MB。
如果您透過ArtManagerLocal#setBatchDexoptStartCallback
自訂套件列表,則BatchDexoptStartCallback
為bg-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 原始程式碼,未發布的開發原始程式碼)。
ArtManagerLocal
是LocalManagerRegistry
持有的單例。輔助函數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
檔的校驗和不符。)