Renderizando o primeiro quadro de um aplicativo Android

imagem



Olá a todos! Esta postagem é uma continuação da postagem sobre um mergulho profundo no processo de carregamento e inicialização de um aplicativo Android. Hoje iremos um pouco mais além e discutiremos o momento em que a Activity principal do aplicativo é iniciada e o sistema deve renderizar o primeiro frame. Por favor, embaixo do gato.



Seguindo a documentação oficial, o processo do aplicativo em execução é responsável por realizar as seguintes etapas:



  1. Criando um objeto da classe Application.
  2. Inicie o tópico principal (MainThread também conhecido como UiThread).
  3. Criação da atividade inicial, que é especificada no manifesto.
  4. Expansão (aumento) de visualizações. Ou seja, a criação de visualizações, que são registradas no arquivo xml.
  5. Layout de tamanhos (View.measure ()) e posicionamento (View.layout ()) de visualizações na tela.
  6. Executando a renderização inicial.


Depois que o primeiro quadro foi desenhado, o processo do sistema substitui a janela de plano de fundo exibida, substituindo-a pela Atividade do aplicativo. O usuário agora pode interagir com o aplicativo.



imagem



Agora, vamos examinar mais de perto todas as etapas.



Início do stream principal



Na postagem anterior, aprendemos:



  • Quando o processo do aplicativo é iniciado, ele chama o método ActivityThread. main () , que faz uma solicitação IPC de bloqueio para ActivityManagerService. attachApplication () no processo system_server .
  • system_server IPC- ActivityThread.bindApplication(), BIND_APPLICATION MessageQueue .
  • IPC- ActivityManagerService.attachApplication() , ActivityThread.main() Looper.loop(), ( ) MessageQueue.
  • , BIND_APPLICATION. ActivityThread.handleBindApplication(), APK .


imagem



Um ponto importante: nada acontece na thread principal do processo de aplicação até que seja feita uma chamada IPC para ActivityManagerService. attachApplication () .



Planejando o lançamento da Atividade



Vamos ver o que acontece no processo system_server após chamar o método ActivityThread. bindApplication () :



public class ActivityManagerService extends IActivityManager.Stub {

  private boolean attachApplicationLocked(
      IApplicationThread thread, int pid, int callingUid,
      long startSeq) {
    thread.bindApplication(...);

    // See if the top visible activity is waiting to run
    //  in this process...
    mAtmInternal.attachApplication(...);

    // Find any services that should be running in this process...
    mServices.attachApplicationLocked(app, processName);

    // Check if a next-broadcast receiver is in this process...
    if (isPendingBroadcastProcessLocked(pid)) {
        sendPendingBroadcastsLocked(app);
    }
    return true;
  }
}


A string relevante para o lançamento da Activity é mAtmInternal. attachApplication (...) . O método chama ActivityTaskManagerService. attachApplication () , que por sua vez chama RootActivityContainer. attachApplication () :



class RootActivityContainer extends ConfigurationContainer {

  boolean attachApplication(WindowProcessController app) {
    for (ActivityDisplay display : mActivityDisplays) {
      ActivityStack stack = display.getFocusedStack()
      ActivityRecord top = stack.topRunningActivityLocked();
      stack.getAllRunningVisibleActivitiesLocked(mTmpActivityList);
      for (ActivityRecord activity : mTmpActivityList) {
        if (activity.app == null
            && app.mUid == activity.info.applicationInfo.uid
            && app.mName.equals(activity.processName)) {
          mStackSupervisor.realStartActivityLocked(
            activity,
            app,
            top == activity /* andResume */,
            true /* checkConfig */
          )
        }
      }
    }
    ...
  }
}


O código faz o seguinte:



  • Ignora todos os monitores.
  • Obtém a pilha de atividades em foco para esta exibição.
  • Faz um loop em cada atividade da pilha de atividades de destino.
  • Se a Activity pertencer a um processo em execução, o método ActivityStackSupervisor é chamado. realStartActivityLocked () . Observe que o parâmetro andResume será verdadeiro se a Activity estiver no topo da pilha.


Essa é a aparência do método ActivityStackSupervisor. realStartActivityLocked () :



public class ActivityStackSupervisor{

  boolean realStartActivityLocked(
    ActivityRecord r,
    WindowProcessController proc,
    boolean andResume,
    boolean checkConfig
  ) {
    ...
    ClientTransaction clientTransaction = ClientTransaction.obtain(
            proc.getThread(), r.appToken);

    clientTransaction.addCallback(LaunchActivityItem.obtain(...));

    // Set desired final state.
    final ActivityLifecycleItem lifecycleItem;
    if (andResume) {
        boolean forward = dc.isNextTransitionForward()
        lifecycleItem = ResumeActivityItem.obtain(forward);
    } else {
        lifecycleItem = PauseActivityItem.obtain();
    }
    clientTransaction.setLifecycleStateRequest(lifecycleItem);

    // Schedule transaction.
    mService.getLifecycleManager()
      .scheduleTransaction(clientTransaction);
    ...
  }
}


Todas as chamadas de método que vimos ocorrem no processo system_server . Método ClientLifecycleManager. scheduleTransaction () faz uma chamada IPC para ActivityThread. scheduleTransaction () no processo de aplicativo que chama ClientTransactionHandler. scheduleTransaction () para enfileirar a mensagem EXECUTE_TRANSACTION :



public abstract class ClientTransactionHandler {

    /** Prepare and schedule transaction for execution. */
    void scheduleTransaction(ClientTransaction transaction) {
        transaction.preExecute(this);
        sendMessage(
          ActivityThread.H.EXECUTE_TRANSACTION,
          transaction
        );
    }
}


Ao processar a mensagem EXECUTE_TRANSACTION , o método TransactionExecutor é chamado. execute () .



Agora você pode atualizar o diagrama:



imagem



Lançamento real da atividade



TransactionExecutor. execute () chama TransactionExecutor.

performLifecycleSequence () , que por sua vez faz um callback no ActivityThread para criar ( criar ), iniciar ( iniciar ) e retomar ( retomar ) a Activity:



public class TransactionExecutor {

  private void performLifecycleSequence(...) {
    for (int i = 0, state; i < path.size(); i++) {
      state = path.get(i);
      switch (state) {
        case ON_CREATE:
          mTransactionHandler.handleLaunchActivity(...);
          break;
        case ON_START:
          mTransactionHandler.handleStartActivity(...);
          break;
        case ON_RESUME:
          mTransactionHandler.handleResumeActivity(...);
          break;
        case ON_PAUSE:
          mTransactionHandler.handlePauseActivity(...);
          break;
        case ON_STOP:
          mTransactionHandler.handleStopActivity(...);
          break;
        case ON_DESTROY:
          mTransactionHandler.handleDestroyActivity(...);
          break;
        case ON_RESTART:
          mTransactionHandler.performRestartActivity(...);
          break;
      }
    }
  }
}


Atualizando o diagrama:



imagem



Primeiro quadro



Vamos dar uma olhada na sequência de chamadas de método que levam à renderização do primeiro quadro:



  • ActivityThread. handleResumeActivity ()
  • WindowManagerImpl. addView ()
  • WindowManagerGlobal. addView ()
  • ViewRootImpl. setView ()
  • ViewRootImpl. requestLayout ()
  • ViewRootImpl. scheduleTraversals ()
  • Coreógrafo. postCallback ()
  • Coreógrafo. scheduleFrameLocked ()


Método coreógrafo. scheduleFrameLocked () enfileira a mensagem MSG_DO_FRAME :



imagem



Ao processar a mensagem MSG_DO_FRAME , o método Choreographer é chamado. doFrame () , que por sua vez chama ViewRootImpl. doTraversal () , que passa na passagem de medida e na passagem de layout e, finalmente, a primeira passagem de desenho pela hierarquia de visualização:



imagem



Conclusão



Começamos com um alto nível de compreensão do que acontece quando o sistema cria o processo do aplicativo:



imagem



Agora sabemos exatamente o que acontece “por baixo do capô”:



imagem



Agora vamos conectar os diagramas do post anterior, desde o momento em que o usuário toca no ícone do aplicativo até o primeiro ser desenhado. quadro:



imagem



Agora que temos o quadro completo, podemos começar a descobrir como controlar adequadamente uma inicialização a frio. O próximo post será sobre isso! Até logo.



All Articles