Rendern des ersten Frames einer Android-Anwendung

Bild



Hallo alle zusammen! Dieser Beitrag ist eine Fortsetzung des Beitrags über einen tiefen Einblick in den Prozess des Herunterladens und Startens einer Android-Anwendung. Heute werden wir etwas weiter gehen und den Moment diskutieren, in dem die Hauptaktivität der Anwendung gestartet wird und das System den ersten Frame rendern sollte. Bitte unter der Katze.



Nach der offiziellen Dokumentation ist der laufende Bewerbungsprozess für die folgenden Schritte verantwortlich:



  1. Erstellen eines Objekts der Application-Klasse.
  2. Starten Sie den Haupt-Thread (MainThread aka UiThread).
  3. Erstellung der Startaktivität, die im Manifest angegeben ist.
  4. Erweiterung (Aufblasen) von Ansichten. Das heißt, die Erstellung von Ansichten, die in der XML-Datei registriert sind.
  5. Layout der Größen (View.measure ()) und Platzierung (View.layout ()) der Ansichten auf dem Bildschirm.
  6. Erstes Rendern durchführen.


Nachdem der erste Rahmen gezogen wurde, ersetzt das System Prozess der Hintergrund - Fenster angezeigt, mit der Ersetzen von Anwendungsaktivität . Der Benutzer kann jetzt mit der Anwendung interagieren.



Bild



Schauen wir uns nun alle Schritte genauer an.



Start des Hauptstroms



Im vorherigen Beitrag haben wir gelernt:



  • , ActivityThread.main(), IPC- ActivityManagerService.attachApplication() system_server.
  • system_server IPC- ActivityThread.bindApplication(), BIND_APPLICATION MessageQueue .
  • Beim IPC-Aufruf von ActivityManagerService. attachApplication () abgeschlossen, ActivityThread. main () ruft Looper auf. loop () , der für immer eine Schleife ausführt (solange die Anwendung ausgeführt wird) und Nachrichten verarbeitet, die in die MessageQueue eingehen .
  • Die erste Nachricht, die verarbeitet wird, ist BIND_APPLICATION . Zu diesem Zeitpunkt wird die ActivityThread-Methode aufgerufen. handleBindApplication () , das die APK und andere Anwendungskomponenten lädt.


Bild



Ein wichtiger Punkt: Im Hauptthread des Anwendungsprozesses geschieht nichts, bis ein IPC-Aufruf des ActivityManagerService erfolgt. attachApplication () .



Planen Sie den Start der Aktivität



Mal sehen, was im system_server-Prozess nach dem Aufrufen der ActivityThread-Methode passiert. 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;
  }
}


Die Zeichenfolge, die für den Start der Aktivität relevant ist, ist mAtmInternal. attachApplication (...) . Die Methode ruft ActivityTaskManagerService auf. attachApplication () , das wiederum den RootActivityContainer aufruft. 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 */
          )
        }
      }
    }
    ...
  }
}


Der Code führt Folgendes aus:



  • Umgeht jede Anzeige.
  • Ruft den Stapel fokussierter Aktivitäten für diese Anzeige ab.
  • Durchläuft jede Aktivität des Zielaktivitätsstapels.
  • Wenn die Aktivität zu einem laufenden Prozess gehört, wird die ActivityStackSupervisor-Methode aufgerufen. realStartActivityLocked () . Beachten Sie, dass der Parameter andResume wahr ist, wenn sich die Aktivität oben im Stapel befindet.


So sieht die ActivityStackSupervisor-Methode aus. 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);
    ...
  }
}


Alle Methodenaufrufe, die wir gesehen haben, treten im Prozess system_server auf . ClientLifecycleManager-Methode. ScheduleTransaction () ruft IPC ActivityThread auf. ScheduleTransaction () im Anwendungsprozess, der ClientTransactionHandler aufruft. ScheduleTransaction (), um die EXECUTE_TRANSACTION- Nachricht in die Warteschlange zu stellen :



public abstract class ClientTransactionHandler {

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


Bei der Verarbeitung der EXECUTE_TRANSACTION- Nachricht wird die TransactionExecutor-Methode aufgerufen. execute () .



Jetzt können Sie das Diagramm aktualisieren:



Bild



Tatsächlicher Start der Aktivität



Methode TransactionExecutor. execute () ruft TransactionExecutor auf.

performLifecycleSequence () , die wiederum einen Rückruf im ActivityThread macht (zum Erstellen erstellen ), Start ( Start ) und Lebenslauf ( Lebenslauf ) , um die Aktivität:



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


Aktualisieren des Diagramms:



Bild



Erster Frame



Schauen wir uns die Abfolge der Methodenaufrufe an, die zum Rendern des ersten Frames führen:



  • ActivityThread. handleResumeActivity ()
  • WindowManagerImpl. addView ()
  • WindowManagerGlobal. addView ()
  • ViewRootImpl. setView ()
  • ViewRootImpl. requestLayout ()
  • ViewRootImpl. ScheduleTraversals ()
  • Choreograph. postCallback ()
  • Choreograph. ScheduleFrameLocked ()


Choreographenmethode. ScheduleFrameLocked () stellt die MSG_DO_FRAME- Nachricht in die Warteschlange :



Bild



Bei der Verarbeitung der MSG_DO_FRAME- Nachricht wird die Choreographer-Methode aufgerufen. doFrame () , das wiederum ViewRootImpl aufruft. doTraversal () , die die Durchläufe Maßübergabe und Layout Pass und schließlich den ersten Streckdurchgang durch die Ansichtshierarchie:



Bild



Fazit



Wir haben mit einem hohen Maß an Verständnis dafür begonnen, was passiert, wenn das System den Bewerbungsprozess erstellt:



Bild



Jetzt wissen wir, was genau „unter der Haube“ passiert:



Bild



Verbinden wir nun die Diagramme aus dem vorherigen Beitrag, von dem Moment an, in dem der Benutzer auf das Anwendungssymbol tippt, bis das erste gezeichnet wird. Rahmen:



Bild



Nachdem wir das vollständige Bild haben, können wir herausfinden, wie ein Kaltstart richtig gesteuert werden kann. Der nächste Beitrag wird darüber sein! Bis dann.



All Articles