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:
- Criando um objeto da classe Application.
- Inicie o tópico principal (MainThread também conhecido como UiThread).
- Criação da atividade inicial, que é especificada no manifesto.
- Expansão (aumento) de visualizações. Ou seja, a criação de visualizações, que são registradas no arquivo xml.
- Layout de tamanhos (View.measure ()) e posicionamento (View.layout ()) de visualizações na tela.
- 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.
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 .
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:
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:
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 :
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:
Conclusão
Começamos com um alto nível de compreensão do que acontece quando o sistema cria o processo do aplicativo:
Agora sabemos exatamente o que acontece “por baixo do capô”:
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:
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.