Прежде чем начать, ознакомьтесь с общим обзором сервиса ART .
Начиная с Android 14, компиляция приложений AOT на устройстве (также известная как dexopt) осуществляется службой ART. Служба ART является частью модуля ART, и вы можете настроить ее через системные свойства и API.
Свойства системы
Сервис ART поддерживает все соответствующие опции dex2oat .
Кроме того, служба ART поддерживает следующие системные свойства:
pm.dexopt.<reason>
Это набор системных свойств, определяющих фильтры компилятора по умолчанию для всех предопределенных причин компиляции, описанных в сценариях 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.<reason>.concurrency (по умолчанию: 1)
Это количество вызовов dex2oat по определенным предопределенным причинам компиляции ( first-boot , boot-after-ota , boot-after-mainline-update и bg-dexopt ).
Обратите внимание, что эффект этой опции суммируется с параметрами использования ресурсов 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и профили задач всегда ограничивают использование ядер ЦП, независимо от максимального количества одновременно работающих потоков (обсуждалось выше).
Один вызов dex2oat может не полностью использовать все ядра ЦП, независимо от dalvik.vm.*dex2oat-threads . Поэтому увеличение количества вызовов dex2oat ( pm.dexopt.<reason>.concurrency ) может лучше использовать ядра ЦП, ускоряя общий процесс 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 , по умолчанию: 500 МБ) плюс 500 МБ.
Если вы настроите список пакетов с помощью ArtManagerLocal#setBatchDexoptStartCallback , то пакеты из списка, предоставленного BatchDexoptStartCallback для bg-dexopt никогда не будут понижены в версии.
pm.dexopt.disable_bg_dexopt (по умолчанию: false)
Это предназначено только для тестирования. Это предотвращает планирование фонового задания dexopt службой ART.
Если фоновое задание dexopt уже запланировано, но еще не выполнено, эта опция не оказывает никакого эффекта. То есть задание все равно будет выполнено.
Рекомендуемая последовательность команд для предотвращения запуска фоновой задачи dexopt:
setprop pm.dexopt.disable_bg_dexopt true
pm bg-dexopt-job --disable
Первая строка предотвращает планирование фоновой задачи dexopt, если она еще не запланирована. Вторая строка отменяет планирование фоновой задачи dexopt, если она уже запланирована, и немедленно отменяет фоновую задачу dexopt, если она выполняется.
API сервиса ART
Сервис ART предоставляет Java API для настройки. API определены в ArtManagerLocal . См. документацию Javadoc в файле art/libartservice/service/java/com/android/server/art/ArtManagerLocal.java для получения информации об использовании ( исходный код 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 для приложения
Вы можете запустить dexopt для любого приложения в любое время, вызвав метод ArtManagerLocal#dexoptPackage .
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();
Вы также можете отменить фоновую оптимизацию (dexopt), инициируемую службой ART.
getArtManagerLocal().cancelBackgroundDexoptJob();
Получите результаты dexopt
Если операция инициируется вызовом функции dexoptPackage , результат можно получить из возвращаемого значения.
DexoptResult result;
try (var snapshot = getPackageManagerLocal().withFilteredSnapshot()) {
result = getArtManagerLocal().dexoptPackage(...);
}
// Process the result here.
...
В ряде сценариев служба ART также самостоятельно инициирует операции dexopt, например, в фоновом режиме. Чтобы отслеживать все результаты dexopt, независимо от того, инициирована ли операция вызовом dexoptPackage или службой ART, используйте ArtManagerLocal#addDexoptDoneCallback .
getArtManagerLocal().addDexoptDoneCallback(
false /* onlyIncludeUpdates */,
Runnable::run,
(result) -> {
// Process the result here.
...
});
Первый аргумент определяет, следует ли включать в результат только обновления. Если вы хотите отслеживать только пакеты, обновленные с помощью dexopt, установите его значение в true.
Второй аргумент — это исполнитель функции обратного вызова. Чтобы выполнить функцию обратного вызова в том же потоке, что и dexopt, используйте Runnable::run . Если вы не хотите, чтобы функция обратного вызова блокировала dexopt, используйте асинхронный исполнитель.
Вы можете добавить несколько функций обратного вызова, и служба ART выполнит их все последовательно. Все функции обратного вызова останутся активными для всех последующих вызовов, если вы их не удалите.
Если вы хотите удалить функцию обратного вызова, сохраните ссылку на неё при добавлении и используйте 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 для этих операций, используйте 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
Любая операция dexopt, инициированная службой ART, запускает вызов 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+)
Вы можете переопределить фильтр компилятора для определенных пакетов, зарегистрировав функцию обратного вызова через setAdjustCompilerFilterCallback . Функция обратного вызова вызывается всякий раз, когда пакет будет подвергнут dexopt, независимо от того, инициируется ли dexopt службой ART во время загрузки и фонового dexopt или вызовом API dexoptPackage .
Если пакет не требует корректировки, функция обратного вызова должна возвращать 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 осуществляется планировщиком заданий. Задание немедленно отменяется, когда температура достигает значения THERMAL_STATUS_MODERATE . Пороговое значение THERMAL_STATUS_MODERATE можно настроить.
Определите, запущена ли фоновая программа dexopt.
Фоновая задача dexopt управляется планировщиком заданий, и ее идентификатор — 27873780 Чтобы определить, выполняется ли задача, используйте 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, запустите dexopt с speed-profile и проверьте результат.
pm art clear-app-profiles <package-name>
pm compile -m speed-profile -f -v <package-name>
Первая строка очищает все профили, созданные средой выполнения (т. е. находящиеся в /data/misc/profiles ), если таковые имеются, чтобы гарантировать, что профиль, расположенный рядом с APK-файлом, является единственным профилем, который может использовать служба ART. Вторая строка запускает dexopt с speed-profile и передает параметр -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 Service не использует этот профиль, включают следующее:
- Профиль имеет неправильное имя файла или не расположен рядом с APK-файлом.
- Профиль имеет неправильный формат.
- Профиль не соответствует APK-файлу. (Контрольные суммы в профиле не совпадают с контрольными суммами файлов
.dexв APK-файле.)