Esta será uma série de posts sobre o processo de inicialização a frio de um aplicativo Android, desde o momento em que você clica no ícone até a criação do processo do aplicativo.
Esquema geral
Abrindo a "janela" ...
Antes de iniciar um novo processo de aplicativo, system_server cria uma janela inicial usando o método PhoneWindowManager .addSplashScreen () :
public class PhoneWindowManager implements WindowManagerPolicy {
public StartingSurface addSplashScreen(...) {
...
PhoneWindow win = new PhoneWindow(context);
win.setIsStartingWindow(true);
win.setType(TYPE_APPLICATION_STARTING);
win.setTitle(label);
win.setDefaultIcon(icon);
win.setDefaultLogo(logo);
win.setLayout(MATCH_PARENT, MATCH_PARENT);
addSplashscreenContent(win, context);
WindowManager wm = (WindowManager) context.getSystemService(
WINDOW_SERVICE
);
View view = win.getDecorView();
wm.addView(view, params);
...
}
private void addSplashscreenContent(PhoneWindow win,
Context ctx) {
TypedArray a = ctx.obtainStyledAttributes(R.styleable.Window);
int resId = a.getResourceId(
R.styleable.Window_windowSplashscreenContent,
0
);
a.recycle();
Drawable drawable = ctx.getDrawable(resId);
View v = new View(ctx);
v.setBackground(drawable);
win.setContentView(v);
}
}
A janela inicial é o que o usuário verá enquanto o aplicativo está em execução. A janela será exibida até que a Atividade seja iniciada e o primeiro quadro seja desenhado. Isto é, até que a inicialização a frio seja concluída . O usuário pode ver esta janela por um longo tempo, então tente torná-la agradável.
O conteúdo da janela inicial é obtido dos recursos drawable windowSplashscreenContent e windowBackground da Activity lançada . Um exemplo trivial de tal janela:
se o usuário restaura a atividade do modo de tela recente , enquanto clica no ícone do aplicativo, então system_serverchama o método TaskSnapshotSurface .create () para criar uma janela inicial a partir de uma captura de tela já tirada.
Uma vez que a janela inicial é mostrada ao usuário, system_server está pronto para iniciar o processo do aplicativo e chama o método ZygoteProcess. startViaZygote () :
public class ZygoteProcess {
private Process.ProcessStartResult startViaZygote(...) {
ArrayList<String> argsForZygote = new ArrayList<>();
argsForZygote.add("--runtime-args");
argsForZygote.add("--setuid=" + uid);
argsForZygote.add("--setgid=" + gid);
argsForZygote.add("--runtime-flags=" + runtimeFlags);
...
return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi),
zygotePolicyFlags,
argsForZygote);
}
}
No código, você pode ver que o ZygoteProcess. zygoteSendArgsAndGetResult () envia argumentos de inicialização através do soquete para o processo Zygote .
"Separação" do zigoto
De acordo com a documentação do Android sobre gerenciamento de memória, segue:
Cada processo de aplicação é iniciado bifurcando (separando) de um processo Zygote existente ...Escrevi brevemente sobre isso no artigo anterior sobre o lançamento do Android . Agora vamos dar uma olhada mais profunda nos processos em andamento.
Quando o sistema é inicializado, o processo Zygote inicia e executa o método ZygoteInit .main () :
public class ZygoteInit {
public static void main(String argv[]) {
...
if (!enableLazyPreload) {
preload(bootTimingsTraceLog);
}
// The select loop returns early in the child process after
// a fork and loops forever in the zygote.
caller = zygoteServer.runSelectLoop(abiList);
// We're in the child process and have exited the
// select loop. Proceed to execute the command.
if (caller != null) {
caller.run();
}
}
static void preload(TimingsTraceLog bootTimingsTraceLog) {
preloadClasses();
cacheNonBootClasspathClassLoaders();
preloadResources();
nativePreloadAppProcessHALs();
maybePreloadGraphicsDriver();
preloadSharedLibraries();
preloadTextResources();
WebViewFactory.prepareWebViewInZygote();
warmUpJcaProviders();
}
}
Como você pode ver o método ZygoteInit. main () faz 2 coisas importantes:
- Carrega todas as bibliotecas e recursos do sistema necessários da estrutura Android. Esse pré-carregamento não apenas economiza memória, mas também economiza tempo de inicialização do aplicativo.
- Em seguida, ele executa o método ZygoteServer.runSelectLoop (), que por sua vez inicia o soquete e começa a ouvir chamadas para esse soquete.
Quando um comando para bifurcar o processo chega ao soquete, o ZygoteConnection.
processOneCommand () processa argumentos usando o método ZygoteArguments. parseArgs () e executa o Zygote. forkAndSpecialize () :
public final class Zygote {
public static int forkAndSpecialize(...) {
ZygoteHooks.preFork();
int pid = nativeForkAndSpecialize(...);
// Set the Java Language thread priority to the default value.
Thread.currentThread().setPriority(Thread.NORM_PRIORITY);
ZygoteHooks.postForkCommon();
return pid;
}
}
Observação: a partir do Android 10, há um recurso de otimização chamado Unspecialized App Process , que possui um pool de processos Zygote não especializados para iniciar aplicativos ainda mais rápido.
O aplicativo foi iniciado!
Após a bifurcação, o processo filho executa o método RuntimeInit. commonInit () , que define o UncaughtExceptionHandler padrão . Em seguida, o processo inicia o método ActivityThread. main () :
public final class ActivityThread {
public static void main(String[] args) {
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
Looper.loop();
}
final ApplicationThread mAppThread = new ApplicationThread();
private void attach(boolean system, long startSeq) {
if (!system) {
IActivityManager mgr = ActivityManager.getService();
mgr.attachApplication(mAppThread, startSeq);
}
}
}
Duas coisas interessantes acontecem aqui:
- ActivityThread.main() (Thread) Looper.loop(), Looper-. ( MainThread- aka UiThread) () . Looper , MessageQueue.
- , ActivityThread.attach() IPC- ActivityManagerService.attachApplication() system_server-, , MainThread .
No processo system_server , o ActivityManagerService. attachApplication () chama o ActivityManagerService. attachApplicationLocked () , que completa a configuração do aplicativo que está sendo iniciado:
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;
}
}
Algumas dicas importantes:
- O processo system_server faz uma solicitação IPC ao método ActivityThread. bindApplication () em nosso processo de aplicação, que roteia a solicitação para o método ActivityThread. handleBindApplication () no aplicativo MainThread .
- Imediatamente depois disso, system_server agenda o lançamento da Pending Activity, Service e BroadcastReciever de nosso aplicativo.
- ActivityThread. handleBindApplication () carrega o arquivo APK e os componentes do aplicativo.
- Os desenvolvedores têm a capacidade de influenciar ligeiramente os processos antes de executar o método ActivityThread. handleBindApplication () , então é aqui que o monitoramento de inicialização a frio do aplicativo deve começar.
Vamos dar uma olhada no terceiro ponto e descobrir o que e como acontece ao carregar componentes e recursos do aplicativo. A ordem das etapas é a seguinte:
- Carregando e instanciando a classe AppComponentFactory .
- Chamando o AppComponentFactory. instantiateClassLoader () .
- Chamando o AppComponentFactory. instantiateApplication () para carregar e instanciar a classe Application .
- Para cada ContentProvider declarado , em ordem de precedência, uma chamada para AppComponentFactory. instantiateProvider () para carregar sua classe e criar uma instância, após chamar o método ContentProvider. onCreate () .
- Finalmente, chamando o aplicativo. onCreate () .
Epílogo
Começamos a explorar a inicialização a frio a partir de um nível abstrato muito geral:
Agora sabemos o que está acontecendo nos bastidores:
Bem, essa foi uma longa postagem. Mas isso não é tudo! Nas próximas postagens, continuaremos nosso mergulho profundo no processo de lançamento de um aplicativo Android. Fique conosco!