Representación del primer fotograma de una aplicación de Android

imagen



¡Hola a todos! Esta publicación es una continuación de la publicación sobre una inmersión profunda en el proceso de carga y lanzamiento de una aplicación de Android. Hoy iremos un poco más allá y discutiremos el momento en el que se lanza la actividad principal de la aplicación y el sistema debería renderizar el primer cuadro. Por favor, debajo del gato.



Siguiendo la documentación oficial, el proceso de aplicación en ejecución es responsable de realizar los siguientes pasos:



  1. Creando un objeto de la clase Aplicación.
  2. Inicie el hilo principal (MainThread también conocido como UiThread).
  3. Creación de la Actividad inicial, que se especifica en el manifiesto.
  4. Expansión (inflación, inflado) de vistas. Es decir, la creación de vistas, que quedan registradas en el archivo xml.
  5. Disposición de tamaños (Ver.medida ()) y ubicación (Ver.disposición ()) de vistas en la pantalla.
  6. Realización de renderizado inicial.


Una vez que se ha dibujado el primer marco, el proceso del sistema reemplaza la ventana de fondo mostrada, reemplazándola con la Actividad de la aplicación. El usuario ahora puede interactuar con la aplicación.



imagen



Ahora echemos un vistazo más de cerca a todos los pasos.



Inicio de la corriente principal



En la publicación anterior, aprendimos:



  • , ActivityThread.main(), IPC- ActivityManagerService.attachApplication() system_server.
  • system_server IPC- ActivityThread.bindApplication(), BIND_APPLICATION MessageQueue .
  • Cuando el IPC llama a ActivityManagerService. attachApplication () completado, ActivityThread. main () llama a Looper. loop () , que se repetirá indefinidamente (mientras la aplicación se esté ejecutando) y procesará los mensajes que lleguen a MessageQueue .
  • El primer mensaje que se procesará es BIND_APPLICATION . En este punto, se llamará al método ActivityThread. handleBindApplication () , que cargará el APK y otros componentes de la aplicación.


imagen



Un punto importante: no sucede nada en el hilo principal del proceso de la aplicación hasta que se realiza la llamada de IPC al método ActivityManagerService. attachApplication () .



Planificación del lanzamiento de la Actividad



Veamos qué sucede en el proceso system_server después de llamar al 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;
  }
}


La cadena que es relevante para el lanzamiento de la actividad es mAtmInternal. attachApplication (...) . El método llama a ActivityTaskManagerService. attachApplication () , que a su vez llama a 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 */
          )
        }
      }
    }
    ...
  }
}


El código hace lo siguiente:



  • Omite todas las pantallas.
  • Obtiene la pila de actividades enfocadas para esta pantalla.
  • Recorre cada actividad de la pila de actividades de destino.
  • Si la actividad pertenece a un proceso en ejecución, se llama al método ActivityStackSupervisor. realStartActivityLocked () . Tenga en cuenta que el parámetro andResume será verdadero si la actividad está en la parte superior de la pila.


Así es como se ve el 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 las llamadas a métodos que hemos visto ocurren en el proceso system_server . Método ClientLifecycleManager. scheduleTransaction () realiza una llamada IPC a ActivityThread. scheduleTransaction () en el proceso de aplicación que llama a ClientTransactionHandler. scheduleTransaction () para poner en cola el mensaje 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
        );
    }
}


Al procesar el mensaje EXECUTE_TRANSACTION , se llama al método TransactionExecutor. ejecutar () .



Ahora puede actualizar el diagrama:



imagen



Lanzamiento real de la actividad



TransactionExecutor. execute () llama a TransactionExecutor.

performLifecycleSequence () , que a su vez realiza una devolución de llamada al ActivityThread para crear ( crear ), iniciar ( iniciar ) y reanudar ( reanudar ) la actividad:



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


Actualizando el diagrama:



imagen



Primer fotograma



Echemos un vistazo a la secuencia de llamadas a métodos que conducen a la representación del primer fotograma:



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


Método de coreógrafo. scheduleFrameLocked () pone en cola el mensaje MSG_DO_FRAME :



imagen



Al procesar el mensaje MSG_DO_FRAME , se llama al método Choreographer. doFrame () , que a su vez llama a ViewRootImpl. doTraversal () , que pasa la pasada de medida y la pasada de diseño , y finalmente la primera pasada de dibujo a través de la jerarquía de vistas:



imagen



Conclusión



Comenzamos con un alto nivel de comprensión de lo que sucede cuando el sistema crea el proceso de la aplicación:



imagen



ahora sabemos qué sucede exactamente "bajo el capó":



imagen



ahora conectemos los diagramas de la publicación anterior, desde el momento en que el usuario toca el ícono de la aplicación hasta que se dibuja el primero. cuadro:



imagen



Ahora que tenemos la imagen completa, podemos comenzar a descubrir cómo controlar adecuadamente un arranque en frío. ¡La próxima publicación será sobre eso! Nos vemos.



All Articles