Прежде чем начать, ознакомьтесь с общим обзором службы ART .
Начиная с Android 14, AOT-компиляция приложений на устройстве (также известная как dexopt) осуществляется службой ART. Служба ART является частью модуля ART, и её можно настраивать через системные свойства и API.
Свойства системы
Служба ART поддерживает все соответствующие опции dex2oat .
Кроме того, ART Service поддерживает следующие системные свойства:
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 Service выполняет компиляцию с учётом профиля ( speed-profile
) для всех приложений, когда это возможно, обычно во время фонового dexopt. Однако некоторые приложения используются другими приложениями (либо через <uses-library>
, либо загружаются динамически с помощью Context#createPackageContext
с CONTEXT_INCLUDE_CODE
). Такие приложения не могут использовать локальные профили из соображений конфиденциальности.
Для такого приложения при запросе компиляции с использованием профиля ART Service сначала пытается использовать облачный профиль. Если облачный профиль отсутствует, ART Service возвращается к использованию фильтра компилятора, указанного в pm.dexopt.shared
.
Если запрошенная компиляция не управляется профилем, это свойство не имеет никакого эффекта.
pm.dexopt.<причина>.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)
Это предназначено только для тестирования. Это не позволяет службе ART планировать фоновое задание dexopt.
Если фоновое задание dexopt уже запланировано, но ещё не запущено, этот параметр не действует. То есть задание всё равно будет запущено.
Рекомендуемая последовательность команд для предотвращения запуска фонового задания dexopt:
setprop pm.dexopt.disable_bg_dexopt true
pm bg-dexopt-job --disable
Первая строка предотвращает планирование фонового задания dexopt, если оно ещё не запланировано. Вторая строка отменяет планирование фонового задания dexopt, если оно уже запланировано, и немедленно отменяет фоновое задание dexopt, если оно запущено.
API-интерфейсы ART-сервисов
Сервис ART предоставляет API Java для настройки. Эти 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());
}
Отменить дексопт
Если операция инициирована вызовом 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 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 для этих операций, используйте 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.
Если отменяемая операция выполняется в фоновом режиме, она следует политике повтора по умолчанию (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
. Обратный вызов вызывается каждый раз при дексоптировании пакета, независимо от того, инициировано ли дексоптирование службой ART во время загрузки и фоновым дексоптированием, или вызовом 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 Service также поддерживает некоторые другие настройки.
Установите тепловой порог для фонового дексопта
Температурный контроль фонового задания 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 Service. Вторая строка запускает 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.)