Otimização de desempenho do Android - monitoramento e resolução de ANR

Autor: Drummor

1 De onde vem o ANR?

ANR (Aplicativo não está respondendo): um erro "Aplicativo não está respondendo" (ANR) é acionado se o thread da interface do usuário de um aplicativo Android for bloqueado por muito tempo. Se o aplicativo estiver em primeiro plano, o sistema exibirá uma caixa de diálogo para o usuário. A caixa de diálogo ANR oferece ao usuário a opção de forçar o encerramento do aplicativo.

ANR é um problema porque o thread principal do aplicativo responsável por atualizar a interface não pode lidar com eventos de entrada do usuário ou operações de desenho, o que deixará os usuários insatisfeitos

A descrição acima é a descrição oficial do ANR. O objetivo do ANR é um tipo de autoproteção do nível do sistema para o comportamento das operações de entrada e desenho da interface que afetam seriamente a percepção direta do usuário.

1.1 Quatro condições

Existem apenas quatro nós que acionam o ANR no nível do sistema. Ou seja, mesmo que o thread principal tenha uma tarefa muito demorada, o ANR não será gerado se não houver nenhuma condição que acione a geração do ANR.

Um ANR é acionado para seu aplicativo quando ocorre qualquer uma das seguintes condições:

  • Insira o tempo limite para agendamento, o limite é de 5 segundos
  • Tempo limite de execução do serviço, falha na conclusão da criação e inicialização do serviço dentro do limite também acionarão o ANR, o limite para tarefas em primeiro plano é 10s e o limite para tarefas em segundo plano é 60s
  • Tempo limite de agendamento do ContentProvider, o limite é 10s
  • Tempo limite de programação BroadcastReceiver, o limite é 10s

1.2 O fluxo geral de ANR gerado pelo sistema

Tome o tempo limite de BroadcastReceive como um exemplo para ver como o sistema aciona o ANR

1.2.1 BroadcastReceiver gera ANR

A lógica principal do broadcast de processamento do BroadcastReceiver está localizada BroadcastQueueem

public final class BroadcastQueue {
        final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {

          setBroadcastTimeoutLocked(timeoutTime);
          ...
          performReceiveLocked(...);//内部最终会调用BroadcastReceiver的onReceiver
          ...
         cancelBroadcastTimeoutLocked();//解除超时
          ..
        }

        // 设置超时
        final void setBroadcastTimeoutLocked(long timeoutTime) {
            if (!mPendingBroadcastTimeoutMessage) {
                Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this);
                mHandler.sendMessageAtTime(msg, timeoutTime);
                mPendingBroadcastTimeoutMessage = true;
            }
        }

      //解除超时
      final void cancelBroadcastTimeoutLocked() {
        if (mPendingBroadcastTimeoutMessage) {
            mHandler.removeMessages(BROADCAST_TIMEOUT_MSG, this);
            mPendingBroadcastTimeoutMessage = false;
        }
}

O exposto acima é a lógica central de configuração, cancelamento e acionamento de ANR no final da transmissão. Atrase o envio de uma [tarefa ANR] por meio do mecanismo do manipulador e conclua a tarefa ANR de remoção da tarefa do broadcast receiver dentro do tempo especificado. Caso contrário, acione.

1.2.1 ANR de processamento do sistema

Na verdade, não importa qual condição acione o ANR, ele será entregue ao AnrHelper para processamento. A lógica do núcleo de processamento do ANR nesta classe inicia um thread denominado "AnrConsumer". Execute o método em ProcessErrorStateRecord appNotResponding().

 void appNotResponding(String activityShortComponentName, ApplicationInfo aInfo,
                          String parentShortComponentName, WindowProcessController parentProcess,
                          boolean aboveSystem, String annotation, boolean onlyDumpSelf) {
        ArrayList<Integer> firstPids = new ArrayList<>(5);
        SparseArray<Boolean> lastPids = new SparseArray<>(20);
        ...
        setNotResponding(true);//标记ANR标识
         ...
        firstPids.add(pid);
        ...
        isSilentAnr = isSilentAnr();//后台的应用发生ANR
        if (!isSilentAnr && !onlyDumpSelf) {//前台进程和不仅仅dump自身时
            mService.mProcessList.forEachLruProcessesLOSP(false, r -> {
              ...
                firstPids.add(pid);//添加其他进程
            }
        }); 
        ...
        StringBuilder report = new StringBuilder();
        report.append(MemoryPressureUtil.currentPsiState());//内存信息
        ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true);//cup信息

        nativePids.add(..); //添加native进程
        ...
        File tracesFile =  .. 
        report.append(tracesFileException.getBuffer());
        info.append(processCpuTracker.printCurrentState(anrTime));
​
        if (tracesFile == null) {
            Process.sendSignal(pid, Process.SIGNAL_QUIT); //不dump信息时直接发送SIGNAL_QUIT信号
        }
        ...
        File tracesFile = ActivityManagerService.dumpStackTraces(..); //dump栈
          ...
        mService.mUiHandler.sendMessageDelayed(msg, anrDialogDelayMs);//ANR弹窗
    }

Pontos notáveis

  • Silent ANR", o ANR de primeiro plano reproduzirá uma caixa de diálogo sem resposta e o ANR de segundo plano matará diretamente o processo**.
  • Ao despejar informações, despeje primeiro as informações do processo ANR e, se as condições permitirem, despeje outros processos associados e processos nativos. Se o processo do sistema tiver muito ANR para processar e demorar mais de 60 segundos ou for um processo silencioso, ele apenas despejará as informações do processo ANR
  • O tempo total de descarregamento de todos os processos não pode exceder 20 segundos. Se exceder, retorne imediatamente para garantir que a janela pop-up ANR possa aparecer a tempo (ou ser eliminada)
  • Process.sendSignal(pid, Process.SIGNAL_QUIT);O sistema emite um sinal Process.SIGNAL_QUIT. Isto é muito importante

(foto da equipe do WeChat)

Quando ocorre um ANR em um aplicativo, o sistema coleta muitos processos e despeja a pilha para gerar um arquivo de rastreamento de ANR. O primeiro processo coletado é o processo que deve ser coletado. Envie o sinal SIGQUIT e o processo do aplicativo começa a despejar a pilha após receber o SIGQUIT.

2 Como monitorar a camada de aplicação

Após a versão Android M (6.0), o lado do aplicativo não pode monitorar diretamente se o ANR ocorre por meio de data/anr/tracearquivos .

2.1 Esquema WatchDog

Também apresentamos essa solução no artigo Caton Monitoring A ideia principal é a detecção de timeout, que detecta se a thread principal MessageQueue processou uma determinada mensagem dentro do tempo especificado (5s). Se a mensagem fornecida não for processada dentro do tempo especificado, considera-se que ocorreu um ANR.

Este esquema é usado para detectar as desvantagens do ANR:

  • Impreciso: a solução não gera necessariamente um ANR se a condição de tempo limite for acionada. O tempo limite de 5 segundos é apenas ToucEventuma condição para ANR se não for consumido. Outras condições que geram ANR não são 5s;
  • Não é elegante: Esta solução manterá o agendamento da mensagem do thread principal em um "estado ocupado" o tempo todo, o que terá um impacto desnecessário no consumo de energia e na carga do aplicativo.

2.2 Esquema de sinal do monitor ( SIGQUIT )

Quando ocorre ANR no sistema, SIGQUITum sinal é enviado. Ao monitorar esse sinal, podemos julgar a ocorrência de ANR. Esta solução é também a principal solução de monitoramento de ANR do mercado.

Com exceção do processo Zygote, cada processo possui SignalCatcherum thread, que capta o sinal SIGQUIT e age de acordo. O Android define SIGQUIT como BLOCKED por padrão, o que significa que apenas SignalCatcherthreads podem ouvir SIGQUITo sinal e sigactionnão podemos nos registrar para ouvi-lo. Definimos SIGQUITpara DESBLOQUEAR para que seja possível receber um sinal. Mas deve-se notar que o sinal precisa ser reenviado sem destruir o mecanismo do sistema.

2.2.1 Falsos Positivos e Perfeição

O sinal enviado pelo sistema SIGQUITnão significa necessariamente que a aplicação possui ANR, em outros casos também será enviado o sinal 'SIGQUIT', por exemplo, ANR ocorre em outros processos.

Encontre a resposta no código-fonte

    private void makeAppNotRespondingLSP(String activity, String shortMsg, String longMsg) {
        setNotResponding(true);
        // mAppErrors can be null if the AMS is constructed with injector only. This will only
        // happen in tests.
        if (mService.mAppErrors != null) {
            mNotRespondingReport = mService.mAppErrors.generateProcessError(mApp,
                    ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING, //把发生ANR的进程
                    activity, shortMsg, longMsg, null);
        }
        startAppProblemLSP();
        mApp.getWindowProcessController().stopFreezingActivities();
    }

Quando ocorrer ANR, o sistema marcará o processo que possui ANR NOT_RESPONDING. Podemos verificar o status através do ActivityManager na camada de aplicação. O código milk é o seguinte:

private static boolean checkErrorState() {
    try {

        ActivityManager am = (ActivityManager) application.getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.ProcessErrorStateInfo> procs = am.getProcessesInErrorState();
        if (procs == null) return false;
        for (ActivityManager.ProcessErrorStateInfo proc : procs) {
            if (proc.pid != android.os.Process.myPid()) continue;
            if (proc.condition != ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING) continue;
            return true;
        }
        return false;
    } catch (Throwable t){
    }
    return false;
}

Depois de receber SIGQUITo sinal, verifique o estado continuamente dentro de um período de tempo. Se o sinalizador for obtido, pode-se considerar que ocorreu ANR no processo atual.

2.2.2 Falso Negativo e Melhoria

Algumas ocorrências de ANR não definirão o processo não NOT_RESPONDINGidentificará

  • Silent ANR ( SilentAnr ), SilentANR matará o processo diretamente e não definirá esse sinalizador.
  • Os modelos Flashback ANR, OPPO VIVO irão piscar diretamente após o ANR, e este sinalizador não será definido.

Solução: combinado com o estado preso do thread principal.

A reflexão obtém o objetoMessageQueue da thread principal . A variável when deste objeto é o tempo em que se espera que a mensagem seja processada. A diferença entre esta variável e o tempo atual pode obter o tempo de espera da mensagem. O atraso demorado , se demorar muito, apenas mMessagesSignifica que a thread principal está 'presa'.

Se for recebido SIGQUITe o thread principal atual estiver travado, considera-se que ocorreu ANR.

2.2.3 Resumo do monitoramento de ANR

Ao monitorar SIGQUITos sinais do sistema combinados com a verificação NOT_RESPONDINGda identificação e do estado travado do thread principal, é determinado de forma abrangente que o ANR ocorreu no processo.

Isso é apenas para sabermos que o ANR ocorreu, sabermos que o ANR ocorreu, sabermos melhor o que causou o ANR, coletar informações de contexto quando o ANR ocorrer e resolver o ANR é mais importante.

3 Coleta e monitoramento de informações

3.1 Dificuldades na localização do problema ANR

A dificuldade de observar a coleta de informações ANR é que a coleta de informações geralmente é imprecisa e incompleta. Quando ocorre ANR, as informações coletadas no momento não são a causa real do ANR, portanto, o valor de referência das informações coletadas para solução de problemas é bastante reduzido.

Conforme mostrado na figura acima, a tarefa demorada no thread principal foi executada e a tarefa de inicialização do serviço gerou ANR quando excedeu o limite especificado. As informações coletadas neste momento são informações de chamada de tarefa normal.

Em geral, as causas do ANR incluem tempo de execução excessivo do thread principal e carga pesada do sistema.

O tempo de execução da tarefa do thread principal é muito longo e pode ser dividido aproximadamente nos seguintes tipos

  • Existem várias mensagens históricas demoradas que acionam o ANR.
  • Há uma notícia extremamente demorada nas notícias históricas.
  • A execução de muitas mensagens pequenas que consomem muito tempo leva muito tempo e aciona o ANR.

A carga do sistema é muito pesada, incluindo memória insuficiente do sistema e carga da CPU, resultando na não execução da tarefa.

Se pudermos registrar mais completamente as tarefas de mensagens históricas do encadeamento principal, as tarefas atuais e futuras e as condições de carregamento do sistema dentro de um período de tempo, será muito importante para nós diagnosticar os problemas de ANR com mais precisão.

3.2 Monitoramento de agendamento de mensagens

A thread principal registra e monitora o plano de execução da mensagem do Looper, e naturalmente voltamos nossa atenção para o plano Printer do Looper. Sobre isso foi introduzido nos dois primeiros artigos do Sanbanfu e não será expandido aqui.

Quando o Looper distribui a mensagem e executa, ele imprime as informações da mensagem antes e depois, e podemos obter as informações relevantes da tarefa da mensagem, incluindo o destino, qual callback da mensagem e o tempo de execução da mensagem.

A mensagem é demorada e o WallTime e o ThreadTime do thread principal precisam ser coletados.

  • WallTime: o tempo gasto pela tarefa, incluindo a espera de bloqueios e o tempo gasto em segmentos de tempo de suspensão do encadeamento.
  • ThreadTime (CpuTime) é o tempo real de execução do thread, excluindo o tempo de espera por bloqueios. Por sua vez, podemos inferir a carga do sistema ao lado.

Na maioria dos casos, a execução da mensagem leva pouco tempo, e o Looper também possui o estado Idel, ou seja, o estado sem execução de mensagem, precisamos agregar essas mensagens.

Além disso, no monitoramento demorado da thread principal da série de artigos Sanbanx, é introduzido que a thread principal processa mensagens. Além das mensagens normalmente distribuídas pelo Looper, que precisam ser monitoradas, o IdleHandler e As mensagens TouchEvent também devem ser incluídas nos registros estatísticos para serem mais completas.

3.2.1 Agregação e classificação de mensagens

  • Estatísticas de agregação de pequenas mensagens contínuas demoradas, mensagens contínuas com menos de 50ms, agregadas em um registro, o número de mensagens armazenadas no registro e informações demoradas totais, etc.
  • Para mensagens que excedem o limite, um único registro é contado.
  • Estatísticas de mensagem de chamada do sistema (ActivityThread.H Activity, Service, ContentProvider), são muito importantes para analisarmos problemas de ANR.
  • As mensagens de status IDLE são contadas separadamente.
  • Estatísticas para IdleHandler.
  • As estatísticas da tarefa de agendamento do thread principal acionadas pela camada nativa, como TouchEvent.

Em geral, classifique as mensagens em tipos, tipos agregados (Concordo) e mensagens contínuas que levam menos tempo. Tipo demorado (enorme): Mensagens superiores a 50ms. Mensagens de chamada do sistema (SYSTEM)

3.2.2 Coleta de pilha de mensagens

Além de registrar estatisticamente o que, o retorno de chamada e o tempo de Looper Messge, também é necessário coletar quais ações são executadas em cada mensagem. Isso requer a coleta da pilha de execução de cada mensagem. A coleta frequente da pilha de execução tem um impacto no desempenho. Os maiores exigem coleta estratégica.

  • Ative o subthread para coletar a pilha do thread principal.
  • As tarefas de mensagem que consomem menos tempo não são coletadas.
  • A tarefa de mensagem que excede um determinado limite e não foi executada é coletada uma vez e, após um período de tempo, se a tarefa de mensagem não tiver sido executada, a coleta é executada novamente e o tempo de intervalo é aumentado linearmente de acordo.
  • [captura de pilha eficiente sem bloqueio do shoppe]

3.2.3 Estatísticas de mensagens em execução e mensagens pendentes

Além de monitorar o agendamento e o consumo de tempo das mensagens históricas do encadeamento principal antes que o ANR ocorra, também é necessário saber as mensagens que estão sendo agendadas e seu consumo de tempo quando o ANR ocorre, para que, quando você vir a pilha de rastreamento de ANR, você pode saber claramente a lógica atual do Trace. quanto tempo demorou

Também é necessário contar as mensagens esperando para serem executadas no MessageQueue

  • Para sabermos quais componentes induzem ANR
  • Você pode contar quanto tempo as mensagens esperando para serem executadas estão esperando. Determine o quão ocupada a fila de mensagens está.

3.3 Coleta de informações mais abrangente

Acima, monitoramos e contamos de forma abrangente o consumo de tempo das tarefas de agendamento do thread principal,

3.3.1 Obter ANRInfo

A camada de aplicação pode obter ANRInfo através do ActivityManager

    val am = application.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
    val processesInErrorStates = am.processesInErrorState

Disponível em ProcessesInErrorState

 shortMsg: ANR Inputdispatching timed out ...
  • shortMessage: A causa direta de ANR, tempo limite do TouchEvent, tempo limite de inicialização do serviço, etc.
Reason: Input dispatching timed out 
xxx is not responding. Waited 5003ms for FocusEvent(hasFocus=false))                                                                                                                                                                          
Load: 3.19 / 2.46 / 2.42
​
----- Output from /proc/pressure/memory -----
some avg10=0.00 avg60=0.00 avg300=0.00 total=144741615
full avg10=0.00 avg60=0.00 avg300=0.00 total=29370150
----- End output from /proc/pressure/memory -----

CPU usage from 0ms to xxx ms later with 99% awake:
​
TOTAL: 6.4% user + 8.2% kernel + 0% iowait + 0.6% irq + 0.1% softirq
​
CPU usage from 0ms to xxx ms later with 99% awake:
​
27% TOTAL: 10% user + 14% kernel + 0.3% iowait + 0.9% irq + 0.3% softirq
  • longMessage: Incluindo carga do sistema, uso do copo, IO e outras condições do sistema.

3.3.2 Registros do Logcat

Colete logs logcat quando o aplicativo estiver em execução, preste atenção à necessidade de controlar a quantidade de políticas correspondentes e limpe regularmente.

String cmd = "logcat -f " + file.getAbsolutePath();
Runtime.getRuntime().exec(cmd);

3.3.3 Informações de empilhamento de outras threads no processo atual

Obtenha cada pilha de encadeamento da camada Java ou obtenha a interface da pilha de encadeamento de despejo dentro da máquina virtual por meio de reflexão e force a interface a ser chamada no endereço da função do mapa de memória e redirecione os dados para a saída local.

Dessa forma, quando o ANR ocorre, temos uma riqueza de informações para nossa referência, incluindo as informações de agendamento passadas, presentes e futuras do encadeamento principal, informações do sistema, informações do encadeamento e informações do Logcat.

4 Análise de problemas, posicionamento e solução

4.1 Análise do escalonamento da thread principal

Verifique nosso agendamento de tarefas de thread principal para ver se há execuções de tarefas demoradas óbvias que induzem ANR.

  • Analise o wallTime e o cputime do nosso agendamento de mensagens gravadas
  • A pilha de mensagens demorada para localizar registros tem um problema com a localização
  • Observe que pode haver muitas mensagens pequenas e demoradas contínuas que também causarão ANR

4.2 Interpretação das Informações ANR

4.2.1 Carga do sistema

Load: xx/xx/xx

A carga da CPU no período de 1 minuto, 5 minutos e 15 minutos antes do ANR ocorrer. O valor representa o número de tarefas aguardando o agendamento do sistema. Se o valor for muito alto, significa que o sistema tem uma competição intensa por CPU e IO, e nosso processo de inscrição pode ser afetado.

4.2.2 Uso da CPU

CPU usage from 0ms to xxx ms later with xx% awake:
​
14% 1673/system_server: 8% user + 6.7% kernel / faults: 12746 minor
13% 30829/tv.danmaku.bili: 7.3% user + 6.2% kernel / faults: 24286 minor
6.6% 31147/tv.danmaku.bili:ijkservice: 3.7% user + 2.8% kernel / faults: 11880 minor
6% 574/logd: 2.1% user + 3.8% kernel / faults: 64 minor
..
TOTAL: 6.4% user + 8.2% kernel + 0% iowait + 0.6% irq + 0.1% softirq
​
CPU usage from xxms to xxxms later 
73% 1673/system_server: 49% user + 24% kernel / faults: 1695 minor
  33% 2330/AnrConsumer: 12% user + 21% kernel
  15% 1683/HeapTaskDaemon: 15% user + 0% kernel
  9.2% 7013/Binder:1673_12: 6.1% user + 3% kernel
  6.1% 1685/ReferenceQueueD: 6.1% user + 0% kernel
  6.1% 2715/HwBinder:1673_5: 6.1% user + 0% kernel
  3% 2529/PhotonicModulat: 0% user + 3% kernel
25% 30829/tv.danmaku.bili: 4.2% user + 21% kernel / faults: 423 minor
  25% 31050/thread_ad: 4.2% user + 21% kernel
  ...
  ...                                                                                                   
27% TOTAL: 10% user + 14% kernel + 0.3% iowait + 0.9% irq + 0.3% softirq

Como acima, indica o uso da CPU antes e depois da ocorrência de ANR e o uso específico desses processos.

  • usuário: modo de usuário
  • kernel: estado do kernel
  • iowait: io espera. Se for muito alto, pode ocorrer leitura e gravação de arquivos ou falta de memória.
  • irq: interrupção forçada
  • softirq: proporção de interrupções suaves

Nota: A carga de uma CPU de processo único não é limitada a 100%, mas há vários núcleos e várias centenas por cento. Por exemplo, o limite superior de 8 núcleos é 800%.

Além disso, o encadeamento crítico da CPU do sistemakswapd é muito grande, geralmente acompanhado pelos recursos de reciclagem do sistema, afetando o processo do aplicativommcqd

A interpretação das informações de ANR pode nos ajudar melhor e de forma mais abrangente a localizar problemas de ANR.

4.3 Mensagem do Logcat

Se a mensagem de impressão do Logcat for gravada online, concentre-se nos seguintes aspectos para ver o problema

  • onTrimeMemory: Contínuo onTrimMemory geralmente indica que a memória do APP é insuficiente ou os recursos do sistema são insuficientes para causar ANR
  • Slow operation Slow deliveryQuando isso ocorre, o desempenho do sistema é limitado.

5 nós

Das causas do ANR, o processamento do sistema ANR, o monitoramento da camada de aplicativo ANR, o agendamento de tarefas do thread principal para monitoramento abrangente do lado do aplicativo do ANR e a coleta de informações do sistema para resolver o ANR, a ideia geral de análise e resolução de problemas de ANR é finalmente dada.

Para o problema de ANR, isso está longe de ser suficiente. Aqui está apenas uma estrutura geral. Espero que este artigo ajude a resolver o problema de ANR de forma mais abrangente.


De acordo com diferentes problemas de monitoramento de desempenho, precisamos adotar diferentes métodos de otimização de desempenho. Atualmente, algumas pessoas não são muito proficientes em alguns métodos de otimização no meio da otimização de desempenho, portanto, todos os diferentes tipos de métodos de otimização no meio da otimização de desempenho são classificados. Classificação de classe, incluindo otimização de inicialização, otimização de memória, otimização de rede, otimização de congelamento, otimização de armazenamento, etc., integrada ao "Manual de pontos de conhecimento do núcleo de otimização de desempenho do Android" , você pode consultar o seguinte:

"Manual avançado de ajuste de desempenho do aplicativo":https://qr18.cn/FVlo89

Otimização de inicialização

Otimização de memória Otimização

de interface do usuário

Otimização de rede

Otimização de bitmap e otimização de compactação de imagem

Otimização de simultaneidade multithread e otimização de eficiência de transmissão de dados

Otimização de pacote de volume

"Resumo das notas principais do ajuste de desempenho do Android":https://qr18.cn/FVlo89

"Estrutura de monitoramento de desempenho do Android":https://qr18.cn/FVlo89

Acho que você gosta

Origin blog.csdn.net/maniuT/article/details/130105015
Recomendado
Clasificación