Kaltstart einer Android-Anwendung

Hallo alle zusammen! Ich habe lange nichts mehr geschrieben.



Dies ist eine Reihe von Beiträgen zum Kaltstart einer Android-Anwendung, vom Klicken auf das Symbol bis zur Erstellung des Anwendungsprozesses.



Bild



Allgemeines Schema



Bild



Das "Fenster" öffnen ...



Vor dem Starten eines neuen Anwendungsprozesses erstellt system_server ein Startfenster mit der PhoneWindowManager .addSplashScreen () -Methode :



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);
  }
}


Das Startfenster wird dem Benutzer angezeigt, während die Anwendung ausgeführt wird. Das Fenster wird angezeigt, bis die Aktivität gestartet und der erste Frame gezeichnet wird. Das heißt, bis der Kaltstart abgeschlossen ist . Der Benutzer kann dieses Fenster lange sehen, versuchen Sie es also angenehm zu gestalten.



Bild



Der Inhalt des Startfensters wird aus den ZeichenressourcenfensternSplashscreenContent und windowBackground der gestarteten Aktivität entnommen . Ein triviales Beispiel für ein solches Fenster:



Bild



Wenn der Benutzer wieder die Aktivität aus dem jüngsten Bildschirm - Modus , während auf das Anwendungssymbol klicken, dann system_serverRuft die TaskSnapshotSurface .create () -Methode auf, um aus einem bereits aufgenommenen Screenshot ein Startfenster zu erstellen.



Sobald dem Benutzer das Startfenster angezeigt wird , kann system_server den Anwendungsprozess starten und ruft die ZygoteProcess-Methode auf. 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);
  }
}


Sie können im Code sehen, dass der ZygoteProcess. zygoteSendArgsAndGetResult () sendet Startargumente über den Socket an den Zygote-Prozess .



Zygotes "Trennung"



Laut Android-Dokumentation zur Speicherverwaltung folgt Folgendes:

Jeder Bewerbungsprozess wird durch Verzweigen (Aufteilen) von einem vorhandenen Zygote-Prozess gestartet ...
Ich habe im vorherigen Artikel über das Starten von Android kurz darüber geschrieben . Schauen wir uns nun die laufenden Prozesse genauer an.



Wenn das System startet , startet der Zygote- Prozess und führt die ZygoteInit .main () -Methode aus :



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();
  }
}


Wie Sie sehen können, ist die ZygoteInit-Methode. main () macht 2 wichtige Dinge:



  • Lädt alle erforderlichen Systembibliotheken und Ressourcen des Android-Frameworks. Ein solches Vorladen spart nicht nur Speicher, sondern auch die Startzeit der Anwendung.
  • Als Nächstes wird die Methode ZygoteServer.runSelectLoop () ausgeführt, die wiederum den Socket startet und auf Aufrufe dieses Sockets wartet.


Wenn ein Befehl zum Verzweigen des Prozesses auf dem Socket, der ZygoteConnection, eintrifft.

processOneCommand () verarbeitet Argumente mit der ZygoteArguments-Methode. parseArgs () und führt die Zygote aus. 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;
  }
}


Bild



Hinweis: Ab Android 10 gibt es eine Optimierungsfunktion namens Unspecialized App Process , die über einen Pool nicht spezialisierter Zygote-Prozesse verfügt, mit denen Anwendungen noch schneller gestartet werden können.



Die Anwendung hat begonnen!



Nach dem Fork führt der untergeordnete Prozess die RuntimeInit-Methode aus. commonInit () , das den Standard- UncaughtExceptionHandler festlegt . Als Nächstes startet der Prozess die ActivityThread-Methode. 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);
    }
  }
}


Hier passieren zwei interessante Dinge:



  • ActivityThread.main() (Thread) Looper.loop(), Looper-. ( MainThread- aka UiThread) () . Looper , MessageQueue.
  • , ActivityThread.attach() IPC- ActivityManagerService.attachApplication() system_server-, , MainThread .


Bild





Im system_server- Prozess der ActivityManagerService. attachApplication () ruft den ActivityManagerService auf. attachApplicationLocked () , mit dem die Konfiguration der zu startenden Anwendung abgeschlossen wird:



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;
  }
}


Ein paar wichtige Imbissbuden:



  • Der Prozess system_server sendet eine IPC-Anforderung an die ActivityThread-Methode. bindApplication () in unserem Anwendungsprozess, der die Anforderung an die ActivityThread-Methode weiterleitet . handleBindApplication () in der MainThread- Anwendung.
  • Unmittelbar danach plant system_server den Start von Pending Activity, Service und BroadcastReciever unserer Anwendung.
  • ActivityThread-Methode. handleBindApplication () lädt die APK-Datei und die Anwendungskomponenten.
  • Entwickler können Prozesse geringfügig beeinflussen, bevor sie die ActivityThread-Methode ausführen. handleBindApplication () , daher sollte hier die Kaltstartüberwachung der Anwendung beginnen.


Bild



Schauen wir uns den dritten Punkt genauer an und finden heraus, was und wie beim Laden von Anwendungskomponenten und Ressourcen passiert. Die Reihenfolge der Schritte ist wie folgt:



  • Laden und Instanziieren der AppComponentFactory- Klasse .
  • Aufruf der AppComponentFactory. instantiateClassLoader () .
  • Aufruf der AppComponentFactory. instantiateApplication () zum Laden und Instanziieren der Application- Klasse .
  • Für jeden deklarierten ContentProvider wird in der Reihenfolge seiner Priorität die AppComponentFactory aufgerufen. instantiateProvider () , um seine Klasse zu laden und eine Instanz zu erstellen, nachdem die ContentProvider-Methode aufgerufen wurde. onCreate () .
  • Zum Schluss die Anwendung aufrufen. onCreate () .


Epilog



Wir haben angefangen, Cold Boot von einer sehr allgemeinen abstrakten Ebene aus zu erforschen:



Bild



Jetzt wissen wir, was unter der Haube vor sich geht:



Bild



Nun, das war ein langer Beitrag. Aber das ist nicht alles! In den nächsten Beiträgen werden wir uns eingehend mit dem Start einer Android-Anwendung befassen. Bleib bei uns!



All Articles