O ProfilingManager oferece suporte à captura de perfis com base em gatilhos do sistema. O
sistema gerencia o processo de gravação e fornece o perfil resultante ao seu
app.
Os acionadores estão vinculados a eventos críticos para o desempenho. Os perfis gravados pelo sistema fornecem informações detalhadas de depuração para as jornadas ideais do usuário (CUJs) associadas a esses gatilhos.
Capturar dados históricos
Muitos acionadores exigem a análise dos dados históricos que antecederam o evento. O gatilho em si geralmente é uma consequência de um problema, e não a causa raiz. Se você iniciar um perfil somente depois que o gatilho ocorrer, a causa raiz já poderá ter sido perdida.
Por exemplo, uma operação de longa duração na linha de execução de interface causa um erro O app não está respondendo (ANR). Quando o sistema detecta o ANR e sinaliza o app, a operação já pode ter sido concluída. Iniciar um perfil nesse momento perde o trabalho de bloqueio real.
É impossível prever exatamente quando alguns gatilhos vão ocorrer, o que impede iniciar um perfil manualmente com antecedência.
Por que usar a captura baseada em acionadores?
O principal motivo para usar gatilhos de criação de perfil é capturar dados de eventos imprevisíveis, em que é impossível para um app começar a gravar manualmente antes que esses eventos ocorram. Os gatilhos de criação de perfil podem ser usados para:
- Depurar problemas de desempenho:diagnostique ANRs, vazamentos de memória e outros problemas de estabilidade.
- Otimize jornadas ideais do usuário:analise e melhore fluxos, por exemplo, a inicialização do app.
- Entender o comportamento do usuário:receba insights sobre eventos, por exemplo, saídas do app iniciadas pelo usuário.
Configurar um gatilho
O código a seguir demonstra como se registrar para o
gatilho TRIGGER_TYPE_APP_FULLY_DRAWN e aplicar a limitação de taxa a ele.
Kotlin
fun recordWithTrigger() { val profilingManager = applicationContext.getSystemService(ProfilingManager::class.java) val triggers = ArrayList<ProfilingTrigger>() val triggerBuilder = ProfilingTrigger.Builder(ProfilingTrigger.TRIGGER_TYPE_APP_FULLY_DRAWN) .setRateLimitingPeriodHours(1) triggers.add(triggerBuilder.build()) val mainExecutor: Executor = Executors.newSingleThreadExecutor() val resultCallback = Consumer<ProfilingResult> { profilingResult -> if (profilingResult.errorCode == ProfilingResult.ERROR_NONE) { Log.d( "ProfileTest", "Received profiling result file=" + profilingResult.resultFilePath ) setupProfileUploadWorker(profilingResult.resultFilePath) } else { Log.e( "ProfileTest", "Profiling failed errorcode=" + profilingResult.errorCode + " errormsg=" + profilingResult.errorMessage ) } } profilingManager.registerForAllProfilingResults(mainExecutor, resultCallback) profilingManager.addProfilingTriggers(triggers)
Java
public void recordWithTrigger() { ProfilingManager profilingManager = getApplicationContext().getSystemService( ProfilingManager.class); List<ProfilingTrigger> triggers = new ArrayList<>(); ProfilingTrigger.Builder triggerBuilder = new ProfilingTrigger.Builder( ProfilingTrigger.TRIGGER_TYPE_APP_FULLY_DRAWN); triggerBuilder.setRateLimitingPeriodHours(1); triggers.add(triggerBuilder.build()); Executor mainExecutor = Executors.newSingleThreadExecutor(); Consumer<ProfilingResult> resultCallback = new Consumer<ProfilingResult>() { @Override public void accept(ProfilingResult profilingResult) { if (profilingResult.getErrorCode() == ProfilingResult.ERROR_NONE) { Log.d( "ProfileTest", "Received profiling result file=" + profilingResult.getResultFilePath()); setupProfileUploadWorker(profilingResult.getResultFilePath()); } else { Log.e( "ProfileTest", "Profiling failed errorcode=" + profilingResult.getErrorCode() + " errormsg=" + profilingResult.getErrorMessage()); } } }; profilingManager.registerForAllProfilingResults(mainExecutor, resultCallback); profilingManager.addProfilingTriggers(triggers);
O código executa estas etapas:
- Get the manager: recupera o serviço
ProfilingManager. - Definir um gatilho: cria um
ProfilingTriggerparaTRIGGER_TYPE_APP_FULLY_DRAWN. Esse evento ocorre quando o app informa que concluiu a inicialização e está interativo. - Definir limites de taxa: aplica um limite de taxa de uma hora a esse gatilho específico
(
setRateLimitingPeriodHours(1)). Isso impede que o app grave mais de um perfil de inicialização por hora. - Registrar listener: chama
registerForAllProfilingResultspara definir o callback que processa o resultado. Esse callback recebe o caminho do perfil salvo porgetResultFilePath(). - Adicionar gatilhos: registra a lista de gatilhos com
ProfilingManagerusandoaddProfilingTriggers. - Ativar evento: chama
reportFullyDrawn(), que emite o eventoTRIGGER_TYPE_APP_FULLY_DRAWNpara o sistema, acionando uma coleta de perfil supondo que um rastreamento em segundo plano do sistema estava em execução e que há uma cota de limitador de taxa disponível. Esta etapa opcional demonstra um fluxo de ponta a ponta porque seu app precisa chamarreportFullyDrawn()para esse gatilho.
Recuperar o trace
O sistema salva os perfis baseados em acionadores no mesmo diretório que outros perfis. O nome do arquivo para rastreamentos acionados segue este formato:
profile_trigger_<profile_type_code>_<datetime>.<profile-type-name>
É possível extrair o arquivo usando o ADB. Por exemplo, para extrair o rastreamento do sistema capturado com o código de exemplo usando o ADB, ele pode ter esta aparência:
adb pull /data/user/0/com.example.sampleapp/files/profiling/profile_trigger_1_2025-05-06-14-12-40.perfetto-trace
Para detalhes sobre como visualizar esses traces, consulte Recuperar e analisar dados de criação de perfil.
Como o rastreamento em segundo plano funciona
Para capturar dados de antes de um gatilho, o SO inicia periodicamente um rastreamento em segundo plano. Se um gatilho ocorrer enquanto esse rastreamento em segundo plano estiver ativo e seu app estiver registrado para ele, o sistema vai salvar o perfil de rastreamento no diretório do app. O perfil vai incluir informações que levaram ao gatilho.
Depois que o perfil é salvo, o sistema notifica seu app usando o callback
fornecido para registerForAllProfilingResults. Esse callback fornece o caminho para
o perfil capturado, que pode ser acessado chamando
ProfilingResult#getResultFilePath().
Para reduzir o impacto no desempenho do dispositivo e na duração da bateria, o sistema não executa rastreamentos em segundo plano continuamente. Em vez disso, ele usa um método de amostragem. O sistema inicia aleatoriamente um rastreamento em segundo plano dentro de um período definido (com uma duração mínima e máxima). O espaçamento aleatório desses rastreamentos melhora a cobertura do gatilho.
Os perfis acionados pelo sistema têm um tamanho máximo definido pelo sistema e, por isso, usam um buffer circular. Quando o buffer está cheio, os novos dados de rastreamento substituem os mais antigos. Como mostrado na Figura 1, um rastreamento capturado pode não cobrir toda a duração da gravação em segundo plano se o buffer ficar cheio. Em vez disso, ele representa a atividade mais recente que antecede o gatilho.
Implementar limitação de taxa específica do acionador
Gatilhos de alta frequência podem consumir rapidamente a cota do limitador de taxa do seu app. Para entender melhor o limitador de taxa, consulte Como ele funciona. Para evitar que um único tipo de gatilho esgote sua cota, implemente a limitação de taxa específica do gatilho.
O ProfilingManager oferece suporte à limitação de taxa específica do gatilho definida pelo app. Isso permite adicionar outra camada de limitação baseada em tempo além do limitador de taxa atual. Use a API setRateLimitingPeriodHours para definir um tempo de
restrição específico para um gatilho. Depois que o período de espera expirar, você poderá acionar o recurso
de novo.
Depurar gatilhos localmente
Como os rastreamentos em segundo plano são executados em horários aleatórios, é difícil acionar a depuração localmente. Para forçar um rastreamento em segundo plano para teste, use o seguinte comando ADB:
adb shell device_config put profiling_testing system_triggered_profiling.testing_package_name <com.example.myapp>
Esse comando força o sistema a iniciar um rastreamento contínuo em segundo plano para o pacote especificado, permitindo que cada acionador colete um perfil se o limitador de taxa permitir.
Você também pode ativar outras opções de depuração, por exemplo, desativar o limitador de taxa ao depurar localmente. Para mais informações, consulte Comandos de depuração para criação de perfil local.