Antes de começar, veja uma visão geral resumida do Serviço ART .
A partir do Android 14, a compilação AOT para aplicativos no dispositivo (também conhecida como dexopt) é feita pelo ART Service. O serviço ART faz parte do módulo ART e você pode personalizá-lo por meio de propriedades do sistema e APIs.
Propriedades do sistema
O ART Service oferece suporte a todas as opções relevantes do dex2oat .
Além disso, o ART Service oferece suporte às seguintes propriedades do sistema:
pm.dexopt.<motivo>
Este é um conjunto de propriedades do sistema que determinam os filtros padrão do compilador para todos os motivos de compilação predefinidos descritos em cenários Dexopt .
Para obter mais informações, consulte Filtros do compilador .
Os valores padrão padrão são:
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 (padrão: velocidade)
Este é o filtro do compilador substituto para aplicativos usados por outros aplicativos.
Em princípio, o ART Service faz compilação guiada por perfil ( speed-profile
) para todos os aplicativos quando possível, normalmente durante o dexopt em segundo plano. No entanto, existem alguns aplicativos que são usados por outros aplicativos (por meio de <uses-library>
ou carregados dinamicamente usando Context#createPackageContext
com CONTEXT_INCLUDE_CODE
). Esses aplicativos não podem usar perfis locais por motivos de privacidade.
Para tal aplicativo, se a compilação guiada por perfil for solicitada, o ART Service primeiro tenta usar um perfil de nuvem. Se um perfil de nuvem não existir, o ART Service voltará a usar o filtro do compilador especificado por pm.dexopt.shared
.
Se a compilação solicitada não for guiada por perfil, esta propriedade não terá efeito.
pm.dexopt.<motivo>.concurrency (padrão: 1)
Este é o número de invocações dex2oat por determinados motivos de compilação predefinidos ( first-boot
, boot-after-ota
, boot-after-mainline-update
e bg-dexopt
).
Observe que o efeito desta opção é combinado com as opções de uso de recursos dex2oat ( dalvik.vm.*dex2oat-threads
, dalvik.vm.*dex2oat-cpu-set
e os perfis de tarefa):
-
dalvik.vm.*dex2oat-threads
controla o número de threads para cada invocação dex2oat, enquantopm.dexopt.<reason>.concurrency
controla o número de invocações dex2oat. Ou seja, o número máximo de threads simultâneos é o produto das duas propriedades do sistema. -
dalvik.vm.*dex2oat-cpu-set
e os perfis de tarefa sempre limitam o uso do núcleo da CPU, independentemente do número máximo de threads simultâneos (discutido acima).
Uma única invocação dex2oat pode não utilizar totalmente todos os núcleos da CPU, independentemente de dalvik.vm.*dex2oat-threads
. Portanto, aumentar o número de invocações dex2oat ( pm.dexopt.<reason>.concurrency
) pode utilizar melhor os núcleos da CPU, para acelerar o progresso geral do dexopt. Isto é particularmente útil durante a inicialização.
No entanto, ter muitas invocações dex2oat pode fazer com que o dispositivo fique sem memória, mesmo que isso possa ser mitigado definindo dalvik.vm.dex2oat-swap
como true
para permitir o uso de um arquivo de troca. Muitas invocações também podem causar trocas desnecessárias de contexto. Portanto, esse número deve ser cuidadosamente ajustado produto por produto.
pm.dexopt.downgrade_after_inactive_days (padrão: não definido)
Se esta opção estiver definida, o ART Service apenas dexopts aplicativos usados nos últimos dias.
Além disso, se o armazenamento estiver quase baixo, durante a dexopt em segundo plano, o ART Service faz downgrade do filtro do compilador de aplicativos que não foram usados nos últimos dias, para liberar espaço. O motivo do compilador para isso é inactive
e o filtro do compilador é determinado por pm.dexopt.inactive
. O limite de espaço para acionar esse recurso é o limite de espaço baixo do Storage Manager (configurável por meio das configurações globais sys_storage_threshold_percentage
e sys_storage_threshold_max_bytes
, padrão: 500 MB) mais 500 MB.
Se você personalizar a lista de pacotes por meio ArtManagerLocal#setBatchDexoptStartCallback
, os pacotes na lista fornecida por BatchDexoptStartCallback
para bg-dexopt
nunca serão rebaixados.
pm.dexopt.disable_bg_dexopt (padrão: falso)
Isto é apenas para teste. Impede que o ART Service agende a tarefa dexopt em segundo plano.
Se o trabalho dexopt em segundo plano já estiver agendado, mas ainda não tiver sido executado, esta opção não terá efeito. Ou seja, o trabalho ainda será executado.
Uma sequência recomendada de comandos para evitar a execução do trabalho dexopt em segundo plano é:
setprop pm.dexopt.disable_bg_dexopt true
pm bg-dexopt-job --disable
A primeira linha impede que o trabalho dexopt em segundo plano seja agendado, se ainda não estiver agendado. A segunda linha desprograma o trabalho dexopt em segundo plano, se já estiver agendado, e cancela o trabalho dexopt em segundo plano imediatamente, se estiver em execução.
APIs de serviço ART
O ART Service expõe APIs Java para personalização. As APIs são definidas em ArtManagerLocal
. Consulte o Javadoc em art/libartservice/service/java/com/android/server/art/ArtManagerLocal.java
para usos ( fonte do Android 14 , fonte de desenvolvimento não lançada ).
ArtManagerLocal
é um singleton mantido por LocalManagerRegistry
. Uma função auxiliar com.android.server.pm.DexOptHelper#getArtManagerLocal
ajuda você a obtê-lo.
import static com.android.server.pm.DexOptHelper.getArtManagerLocal;
A maioria das APIs requer uma instância de PackageManagerLocal.FilteredSnapshot
, que contém as informações de todos os aplicativos. Você pode obtê-lo chamando PackageManagerLocal#withFilteredSnapshot
, onde PackageManagerLocal
também é um singleton mantido por LocalManagerRegistry
e pode ser obtido em com.android.server.pm.PackageManagerServiceUtils#getPackageManagerLocal
.
import static com.android.server.pm.PackageManagerServiceUtils.getPackageManagerLocal;
A seguir estão alguns casos de uso típicos das APIs.
Acionar dexopt para um aplicativo
Você pode acionar o dexopt para qualquer aplicativo a qualquer momento chamando ArtManagerLocal#dexoptPackage
.
try (var snapshot = getPackageManagerLocal().withFilteredSnapshot()) {
getArtManagerLocal().dexoptPackage(
snapshot,
"com.google.android.calculator",
new DexoptParams.Builder(ReasonMapping.REASON_INSTALL).build());
}
Você também pode passar seu próprio motivo dexopt. Se você fizer isso, a classe de prioridade e o filtro do compilador deverão ser definidos explicitamente.
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());
}
Cancelar dexopt
Se uma operação for iniciada por uma chamada dexoptPackage
, você poderá passar um sinal de cancelamento, que permitirá cancelar a operação em algum momento. Isso pode ser útil ao executar o dexopt de forma assíncrona.
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();
Você também pode cancelar o dexopt em segundo plano, que é iniciado pelo ART Service.
getArtManagerLocal().cancelBackgroundDexoptJob();
Obtenha resultados dexopt
Se uma operação for iniciada por uma chamada dexoptPackage
, você poderá obter o resultado do valor de retorno.
DexoptResult result;
try (var snapshot = getPackageManagerLocal().withFilteredSnapshot()) {
result = getArtManagerLocal().dexoptPackage(...);
}
// Process the result here.
...
O ART Service também inicia operações de dexopt em muitos cenários, como dexopt em segundo plano. Para ouvir todos os resultados do dexopt, seja a operação iniciada por uma chamada dexoptPackage
ou pelo serviço ART, use ArtManagerLocal#addDexoptDoneCallback
.
getArtManagerLocal().addDexoptDoneCallback(
false /* onlyIncludeUpdates */,
Runnable::run,
(result) -> {
// Process the result here.
...
});
O primeiro argumento determina se devem ser incluídas apenas atualizações no resultado. Se você deseja apenas ouvir pacotes atualizados pelo dexopt, defina-o como verdadeiro.
O segundo argumento é o executor do retorno de chamada. Para executar o retorno de chamada no mesmo thread que executa o dexopt, use Runnable::run
. Se você não quiser que o retorno de chamada bloqueie o dexopt, use um executor assíncrono.
Você pode adicionar vários retornos de chamada e o ART Service executará todos eles sequencialmente. Todos os retornos de chamada permanecerão ativos para todas as chamadas futuras, a menos que você os remova.
Se você deseja remover um retorno de chamada, mantenha a referência do retorno de chamada ao adicioná-lo e use 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);
Personalize a lista de pacotes e os parâmetros dexopt
O ART Service inicia as próprias operações de dexopt durante a inicialização e o dexopt em segundo plano. Para personalizar a lista de pacotes ou os parâmetros dexopt para essas operações, use 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.
}
});
Você pode adicionar itens à lista de pacotes, remover itens dela, classificá-la ou até mesmo usar uma lista completamente diferente.
Seu retorno de chamada deve ignorar motivos desconhecidos porque mais motivos poderão ser adicionados no futuro.
Você pode definir no máximo um BatchDexoptStartCallback
. O retorno de chamada permanecerá ativo para todas as chamadas futuras, a menos que você o apague.
Se você quiser limpar o retorno de chamada, use ArtManagerLocal#clearBatchDexoptStartCallback
.
getArtManagerLocal().clearBatchDexoptStartCallback();
Personalize os parâmetros do trabalho dexopt em segundo plano
Por padrão, o trabalho dexopt em segundo plano é executado uma vez por dia quando o dispositivo está ocioso e carregando. Isso pode ser alterado usando ArtManagerLocal#setScheduleBackgroundDexoptJobCallback
.
getArtManagerLocal().setScheduleBackgroundDexoptJobCallback(
Runnable::run,
builder -> {
builder.setPeriodic(TimeUnit.DAYS.toMillis(2));
});
Você pode definir no máximo um ScheduleBackgroundDexoptJobCallback
. O retorno de chamada permanecerá ativo para todas as chamadas futuras, a menos que você o apague.
Se você quiser limpar o retorno de chamada, use ArtManagerLocal#clearScheduleBackgroundDexoptJobCallback
.
getArtManagerLocal().clearScheduleBackgroundDexoptJobCallback();
Desative temporariamente o dexopt
Qualquer operação dexopt iniciada pelo ART Service aciona um BatchDexoptStartCallback
. Você pode continuar cancelando as operações para desabilitar efetivamente o dexopt.
Se a operação cancelada for dexopt em segundo plano, ela seguirá a política de repetição padrão (30 segundos, exponencial, limitado a 5 horas).
// 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);
Você pode ter no máximo um BatchDexoptStartCallback
. Se você também quiser usar BatchDexoptStartCallback
para personalizar a lista de pacotes ou os parâmetros dexopt, deverá combinar o código em um retorno de chamada.
// Bad example.
// Disable dexopt.
getArtManagerLocal().unscheduleBackgroundDexoptJob();
// Re-enable dexopt.
getArtManagerLocal().scheduleBackgroundDexoptJob();
A operação dexopt realizada na instalação do aplicativo não é iniciada pelo ART Service. Em vez disso, ele é iniciado pelo gerenciador de pacotes por meio de uma chamada dexoptPackage
. Portanto, não aciona BatchDexoptStartCallback
. Para desabilitar o dexopt na instalação do aplicativo, evite que o gerenciador de pacotes chame dexoptPackage
.
Substitua o filtro do compilador para determinados pacotes (Android 15 (AOSP experimental)+)
Você pode substituir o filtro do compilador para determinados pacotes registrando um retorno de chamada por meio de setAdjustCompilerFilterCallback
. O retorno de chamada é chamado sempre que um pacote será dexopt, independentemente de o dexopt ser iniciado pelo ART Service durante a inicialização e o dexopt em segundo plano ou por uma chamada de API dexoptPackage
.
Se um pacote não precisar de ajustes, o retorno de chamada deverá retornar originalCompilerFilter
.
getArtManagerLocal().setAdjustCompilerFilterCallback(
Runnable::run,
(packageName, originalCompilerFilter, reason) -> {
if (isVeryImportantPackage(packageName)) {
return "speed-profile";
}
return originalCompilerFilter;
});
Você pode definir apenas um AdjustCompilerFilterCallback
. Se quiser usar AdjustCompilerFilterCallback
para substituir o filtro do compilador para vários pacotes, você deverá combinar o código em um retorno de chamada. O retorno de chamada permanece ativo para todas as chamadas futuras, a menos que você o apague.
Se você quiser limpar o retorno de chamada, use ArtManagerLocal#clearAdjustCompilerFilterCallback
.
getArtManagerLocal().clearAdjustCompilerFilterCallback();
Outras personalizações
O ART Service também oferece suporte a algumas outras personalizações.
Defina o limite térmico para o dexopt de fundo
O controle térmico do trabalho dexopt em segundo plano é realizado pelo Job Scheduler. O trabalho é cancelado imediatamente quando a temperatura atinge THERMAL_STATUS_MODERATE
. O limite de THERMAL_STATUS_MODERATE
é ajustável.
Determine se o dexopt em segundo plano está em execução
O trabalho dexopt em segundo plano é gerenciado pelo Job Scheduler e seu ID de trabalho é 27873780
. Para determinar se o trabalho está em execução, use APIs do Job Scheduler.
// 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.
...
}
Forneça um perfil para dexopt
Para usar um perfil para guiar o dexopt, coloque um arquivo .prof
ou .dm
próximo ao APK.
O arquivo .prof
deve ser um arquivo de perfil em formato binário e o nome do arquivo deve ser o nome do APK + .prof
. Por exemplo,
base.apk.prof
O nome do arquivo .dm
deve ser o nome do arquivo do APK com a extensão substituída por .dm
. Por exemplo,
base.dm
Para verificar se o perfil está sendo usado para dexopt, execute dexopt com speed-profile
e verifique o resultado.
pm art clear-app-profiles <package-name>
pm compile -m speed-profile -f -v <package-name>
A primeira linha limpa todos os perfis produzidos pelo tempo de execução (ou seja, aqueles em /data/misc/profiles
), se houver, para garantir que o perfil próximo ao APK seja o único perfil que o ART Service pode usar. A segunda linha executa dexopt com speed-profile
e passa -v
para imprimir o resultado detalhado.
Se o perfil estiver sendo usado, você verá actualCompilerFilter=speed-profile
no resultado. Caso contrário, você verá actualCompilerFilter=verify
. Por exemplo,
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}
Os motivos típicos pelos quais o ART Service não usa o perfil incluem os seguintes:
- O perfil tem um nome de arquivo errado ou não está próximo ao APK.
- O perfil está no formato errado.
- O perfil não corresponde ao APK. (As somas de verificação no perfil não correspondem às somas de verificação dos arquivos
.dex
no APK.)