May 3, 2016 1:30 pm

El ciclo de vida de una aplicación de la Plataforma Universal Windows

La administración del ciclo de vida de una aplicación en la Plataforma Universal Windows (UWP, por sus siglas en inglés) es fundamental para el uso eficiente de la energía de la batería y la memoria. Cuando se hace de manera correcta, permite al usuario realizar múltiples tareas entre aplicaciones sin necesidad de saber nada acerca de la optimización de recursos.

El artículo de hoy es sobre el ciclo de vida de una aplicación UWP, que ocurre como la transición de un estado de su ciclo de vida al siguiente, y las técnicas utilizadas para que estas transiciones sean claras para los usuarios.

Estados de la aplicaciones: sin ejecutarse, ejecutable y suspendida

Las aplicaciones tradicionales de escritorio sólo tienen dos estados: están funcionando bien o no se están ejecutando. Las UWP apps tienen un tercer estado posible llamado suspendida. Una aplicación se suspende cuando un usuario la minimiza, o cambia a otra aplicación. En lo que concierne al usuario, la aplicación simplemente parece estar funcionando en segundo plano.

Lo que realmente ocurre es que la aplicación entra en un modo de hibernación para que Windows sea capaz de liberar recursos. La aplicación se detiene y el estado de la misma se lleva a cabo en la memoria. Cuando el usuario vuelve a la aplicación, se restaura de manera rápida desde un estado de suspensión a un estado de ejecución. El usuario no es consciente de que la suspensión sucedió en algún momento.

1_life-cycle-of-a-UWP-app

En ocasiones, Windows 10 puede determinar que, incluso en un estado de suspensión, algunas aplicaciones utilizan demasiados recursos. En este caso, se pueden dar por terminadas algunas de estas aplicaciones, colocándolas de forma eficiente en un estado en el que no funcionen. ¿Qué sucederá cuando el usuario intente cambiar a una aplicación terminada?

Sin codificación adicional, al usuario le aparecerá como si hubiera perdido su aplicación, así como toda su información de la última sesión.

Ustedes pueden evitar que esto suceda al usuario mediante el control del evento Application.Suspending (no hay ningún método de evento terminado, por lo que este es en este último punto en el que se puede hacer algo) y reemplazando el método Application.OnLaunched cuando la aplicación vuelva a recuperarse. El patrón de codificación para la administración de posibles terminaciones de aplicaciones solo se requiere para guardar fuera de los datos útiles de la sesión del usuario actual. Entonces, si la aplicación se termina, se restauran estos datos con código personalizado una vez que la aplicación se vuelva a lanzar por el usuario.

Manipulación de suspensión, terminación y cierre

Hay varias maneras de que una aplicación puede dejar el estado de ejecución. Ya hemos hablado de un par de ellas, pero hay dos escenarios adicionales. Uno se da cuando una aplicación se mata y va desde de un estado ejecutable a no ejecutable, ya sea porque algo ha ido mal con la aplicación, como un fallo, o porque el usuario ha obligado al cierre de la aplicación en el administrador de tareas. (Que la aplicación esté muerta, no es un término oficial, pero es muy conveniente para recordar este estado del ciclo de vida.) La otra se produce cuando una aplicación se cierra porque el usuario utiliza un gesto de cerca o mientras, en el modo de tableta, haciendo clic en la X cuando se ejecuta sobre el escritorio. En este caso, la aplicación se suspende y luego termina automáticamente.

Ahora vamos a discutir los escenarios en los que se suspende una aplicación y después se termina. En la práctica, esto significa que nos importa si la aplicación es terminada por el sistema operativo (primer escenario) y si el usuario cierra la aplicación de manera intencional (último escenario).

Cuando se suspende una aplicación, se invoca el evento de suspensión. Las plantillas de proyecto de Visual Studio UWP generan automáticamente un controlador de este evento llamado OnSuspending en la clase App.xaml. Es aquí donde se debe colocar el código para guardar la información de estado de la aplicación en caso de que la aplicación termine.

Si la aplicación no cierra después de la suspensión, entonces no se dañará ya que el estado de la aplicación no se pierde nunca.

Del mismo modo, si la aplicación se cierra después de un estado de ejecución sin suspensión, no es necesario hacer nada. De hecho, no hay nada que hacer, ya que hay eventos en el ciclo de vida que no se invocan.

Aquí hay un código de ejemplo que demuestra cómo guardar información de estado. Dado que guardar en FileStream es un proceso asíncrono, se dará cuenta de la adición de la palabra clave asíncrono para la implementación predeterminada del método OnSuspending. Debido a que el objeto _Store utilizado es un diccionario genérico, cualquier tipo de datos seriales puede ser añadido a la misma para guardarlos. Por razones de brevedad, sólo se está añadiendo una marca de tiempo. (También hay algo de código para algo llamado aplazamiento, que abordaremos más adelante.)

El plazo de suspensión y aplazamiento

Hay una regla implícita de que el controlador para el evento Application.Suspending debe terminar dentro de los cinco segundos. Esta regla de cinco segundos se conoce como la fecha límite. De forma normal, con una llamada asincrónica, el subproceso actual volvería a la persona que llama cuando teclea la palabra clave await. Esto estaría mal en el código anterior, ya que el método asíncrono parada es poco probable que haya terminado, pero el control del tiempo vuelve a la persona que llama y la aplicación se suspende.

El método GetDeferral es un método de limpieza utilizado para esta situación. Se dice que el sistema no levantará la suspensión cuando la llamada asíncrona vuelve, pero en lugar de aplazar la suspensión hasta que se llame, ya sea el método completo o la fecha límite de cinco segundos llega, lo que ocurra primero. Esto significa que GetDeferral en realidad no le proporciona más tiempo al usuario para que concluya el estado de guardar. Se limita a indicar cómo se está manejando la limpieza antes de la suspensión y asegura que se le da tanto tiempo como sea necesario, hasta un máximo de cinco segundos, para hacer esto.

Debido a la fecha límite, no deben tratar de hacer demasiado en el controlador OnSuspending. Una estrategia para la gestión de este límite de tiempo es para guardar los datos que se incrementan a medida que cambia el estado de la aplicación. Nuevos pares de valores clave se pueden añadir gradualmente a su objeto de diccionario en los eventos de la página de navegación. Si están utilizando el patrón MVVM para administrar el estado, incluso se podría solo esconder su vista en los modelos en el diccionario como ahorro de tiempo.

Si todo lo demás falla, es posible ganar más tiempo para terminar la operación de salvar al solicitar una ExtendedExecutionSession, aunque no hay ninguna garantía de que la solicitud sea respetada. Pueden aprender más acerca de extended execution al visitar MSDN.

Manejar el estado de arranque en el ciclo de vida de la aplicación

Hemos estado trabajando a través del ciclo de vida de UWP un poco hacia atrás hasta este punto. De hecho, hay una buena razón para esto. Gran parte de la información proporcionada en el método Application.OnLaunched se ocupa de la forma en que la aplicación se cerró de manera previa.

Lanzar y reanudar corresponden a cancelar y suspender, respectivamente. Si una aplicación se mueve desde un estado suspendido de nuevo a un estado de ejecución, se invocará el evento Application.Resuming. Si una aplicación se termina o apaga forzosamente, entonces se llamará Application.OnLaunched.

OnLaunched también se llama cuando una aplicación se invoca inicialmente a partir de un mosaico. Dentro del método OnLaunched, lo que necesita para llevar a cabo rutinas de inicialización para hacer que su aplicación se reanime. Esto puede incluir la realización de las conexiones de red, la asignación de recursos o la creación de la interfaz de usuario. Lo que hay que configurar en segundo plano cuando la aplicación se despierta por primera vez, probablemente se debe configurar, no importa cómo se haya cerrado la aplicación previamente.

El método OnLaunched recibe un objeto LaunchActivatedEventArgs como argumento. Este tiene una propiedad interesante llamada PreviousExecutionState, que le indica el último estado de la aplicación antes de su lanzamiento.

  • NotRunning indica que la aplicación está siendo puesta en marcha por primera vez o que la aplicación fue cerrada con anterioridad de una manera inesperada y que sólo debe iniciar la aplicación como si estuviera siendo ejecutada por primera vez. No se necesita hacer algo hacer para manejar esta situación, y solo necesitan lanzar la aplicación de manera normal.
  • Terminated dice que la aplicación fue suspendida previamente y luego se apagará. En el mundo del usuario, éste era multitarea y después de un tiempo decidió volver a la aplicación. Por desgracia, la aplicación había sido suspendida y luego terminada por el sistema operativo con el fin de liberar memoria. El usuario, sin embargo, no sabe nada de esto y espera que la aplicación se encuentre en el estado que la dejó previamente.
  • ClosedByUser indica que el usuario cierra la aplicación de manera intencional, por ejemplo mediante el uso de gestos en el modo tableta.
  • Runnig and Suspended son los dos últimos estados y dirán que la aplicación está siendo lanzada desde un mosaico secundario cuando ya se ha activado.

Para asegurarse de que el usuario tiene la experiencia esperada, es necesario restaurar la información de estado que se guardó antes de que la aplicación fuera suspendida. El código siguiente muestra cómo se invierte la rutina que se ha utilizado en el manejador Suspending.

Por lo general se desea restaurar todo el estado guardado en la aplicación. Esto supone que el usuario está volviendo a la aplicación después de sólo unos pocos minutos o unas pocas horas. De hecho, podría ser mucho más tiempo desde que el usuario no veía su app, quizás en días.

Debido a que se tuvo la previsión de serializar la fecha y hora en que se suspende la aplicación, se puede comparar con la fecha y hora actuales para determinar cuánto tiempo hace que se dio por terminada la aplicación. Si el tiempo de inactividad es muy grande, puede tener más sentido tratar este lanzamiento aplicación como si fuera por primera vez.

Otro valor posible para el estado de ejecución anterior, ClosedByUser, obliga a tomar alguna iniciativa para decidir lo que significa. Este estado se produce cuando el usuario ha utilizado el cierre con gestos en modo tableta para finalizar la aplicación. Esta acción suspende la aplicación, asegurando que el método OnSuspending sea requerido y termine la aplicación. Dado que la aplicación ha ido esencialmente por los mismos pasos que conducen al estado terminado, la orientación general es manejar esta historia del ciclo de vida de la misma manera.

Por otro lado, si un usuario ha apagado intencionalmente una aplicación, sería razonable esperar para que haga un inicio fresco cuando se relanza. Si esto tiene más sentido para ustedes como desarrolladores, entonces en este sentido pueden tratar con ClosedByUser.

Probar y depurar los estados del ciclo de vida

Es una buena idea poner a prueba los diversos estados del ciclo de vida de la aplicación para asegurarse de que la aplicación hace lo que se espera que haga. Visual Studio 2015 proporciona un control para el ciclo de vida de los eventos en la barra de herramientas, lo que permite lanzar una de las tres transiciones del ciclo de vida: suspender, reanudar o suspender y apagar (terminar).

2_life-cycle-of-a-UWP-app-events-control

Con esta herramienta, pueden depurar el código de gestión de ciclo de vida para asegurarse de que se ocupa de hacer los cambios de estado correctamente. También se puede utilizar para validar su comprensión del propio ciclo de vida. Por ejemplo, ustedes podrían averiguar lo que realmente sucede cuando alguien termina su aplicación en un teléfono usando el conmutador de aplicaciones. ¿Se llamará al manejador OnSuspending en esta situación o la aplicación se cerrará sin que lo sepan? Averigüemos.

3_life-cycle-of-a-UWP-app-deferral

Para probar esto, establezcan un punto de interrupción dentro del método OnSuspending. A continuación, deberán establecer el destino de generación a un emulador móvil y ejecutar la aplicación en modo de depuración. Seleccionen Suspend en la herramienta Lifecycle Events para confirmar que el punto de interrupción es atacado. Presionen F5 para continuar más allá del punto de ruptura y seleccionen Resume en la herramienta para llevar la aplicación de nuevo a la vista.

4_life-cycle-of-a-UWP-app_app-into-view

Ahora viene la parte divertida. Mantengan pulsado el botón de retroceso hasta que la aplicación se contraiga en la pantalla del emulador móvil. Aparece una X en la esquina superior derecha de la aplicación que les permite cerrarla. Cuando se pulsa X, el punto de interrupción no se da un golpe en esta ocasión.

Esta sencilla prueba les indica que la aplicación no entra en modo de suspensión cuando se cerró en la pantalla de conmutador de aplicaciones del teléfono, pero en cambio, va directamente a un estado de paro.

Ustedes encontrarán que hay momentos en los que no puede recordar bastante cómo funciona el ciclo de vida de la UWP en cada circunstancia. En esas situaciones, a menudo tiene más sentido que acaben de poner a prueba sus hipótesis con las herramientas de depuración que Visual Studio proporciona, que intentar calcular hacia fuera por la búsqueda a través de artículos en línea. Pueden encontrar que entienden el ciclo de vida de aplicaciones UWP mejor que cuando lo prueban por ustedes mismos.

Manipulación continúa

Cuando una aplicación se suspende y luego regresa con éxito al estado de ejecución, el evento Application.Resuming se llama Application.OnLaunched. ¿Qué deben hacer con Application.Resuming?

En muchos casos, ustedes no tienen que hacer nada en absoluto. Application.Suspending se utiliza generalmente sólo para manejar la posibilidad de que la aplicación puede estar finalizada. Si desean que su aplicación sea un buen ciudadano de UWP, es posible, también, utilizar Application.Suspending para desasignar recursos valiosos cuando su aplicación entra en hibernación. Los recursos pesados pueden incluir cualquier cosa de las imágenes y archivos de sonido en los dispositivos. Si deciden hacer esto, tendrán que implementar un controlador para el evento Application.Resuming y reinicializar esos recursos.

Hay otra circunstancia excepcional que deben tener en cuenta para sus aplicaciones UWP: cuando un usuario inicia una aplicación que ya ha sido suspendida, por lo que la aplicación llama al evento Application.Resuming. Esto ocurre cuando la aplicación ha sido seleccionada para pre-lanzamiento, que es para toda intensión y propósito, transparente para el usuario.

El pre-lanzamiento sucede si el dispositivo tiene recursos suficientes y la aplicación se determina para ser lanzada con frecuencia por el usuario actual. Para mejorar el rendimiento, las aplicaciones seleccionadas de pre-lanzamiento se ponen en marcha en segundo plano antes de tiempo y después suspendidas de manera rápida.

Debido al lanzamiento previo, la app de OnLaunched puede ser iniciada por el sistema en lugar de un usuario. Esto es seguido por una llamada a Application.Suspending. Entonces, cuando el usuario intenta iniciar la aplicación, el evento de reanudación es invocado, así como el método OnLaunched.

Es sorprendente que no hay pasos adicionales necesarios para manejar el lanzamiento previo. El código de ciclo de vida que ya hemos cubierto se encarga muy bien de todo. En algunas circunstancias, por ejemplo, cuando una aplicación utiliza los recursos pesados ​​en el lanzamiento para crear un impacto visual, puede ser útil para distinguir un lanzamiento previo de un lanzamiento normal, esto para que el esfuerzo no se desperdicie.

El objeto LaunchActivatedEventArgs tiene una propiedad booleana llamada PrelaunchActivated que indica si OnLaunched está respondiendo a un usuario real o para la optimización previa al lanzamiento. Esta bandera se puede utilizar para pasar por encima de cualquier acción que realicen cuando no hay nadie que los vea. Al igual que con los eventos del ciclo de vida estándar, Visual Studio también hace que sea posible probar un escenario de pre-lanzamiento. Ustedes pueden aprender más acerca de prelaunch en MSDN.

En resumen

El ciclo de vida del UWP puede parecer complicado al principio. Sin embargo, en realidad, los dos únicos eventos que suelen tener que preocuparse acerca de la manipulación son Suspending y Launched. Otros indicadores, como PrelaunchActivated y ClosedByUser, son dignos de consideración para los casos extremos, pero no siempre son esenciales. Recuerden también, que si empiezan a sospechar que esos casos extremos pueden estar afectando su aplicación, Visual Studio 2015 proporciona herramientas útiles para ayudar a probar los eventos del ciclo de vida y averiguar lo que está sucediendo en un segundo plano.