Procesamiento en segundo plano de iOS con tareas

La manera más sencilla de realizar tareas en segundo plano en iOS es dividir los requisitos en segundo plano en tareas y ejecutar las tareas en segundo plano. Las tareas están por debajo de un límite de tiempo estricto y normalmente obtienen unos 600 segundos (10 minutos) de tiempo de procesamiento después de que una aplicación se haya movido al segundo plano en iOS 6 y menos de 10 minutos en iOS 7+.

Las tareas en segundo plano se pueden dividir en tres categorías:

  1. Tareas Background-Safe: se llaman en cualquier lugar de la aplicación en la que tenga una tarea que no quiera interrumpir si la aplicación entra en segundo plano.
  2. Tareas DidEnterBackground: se llaman durante el método de ciclo de vida de la aplicación DidEnterBackground para ayudar a limpiar y guardar el estado.
  3. Transferencias en segundo plano (iOS 7+): un tipo especial de tarea en segundo plano que se usa para realizar transferencias de red en iOS 7. A diferencia de las tareas normales, las transferencias en segundo plano no tienen un límite de tiempo determinado previamente.

Las tareas Background-safe y DidEnterBackground son seguras para su uso tanto en iOS 6 como en iOS 7, con algunas diferencias menores. Vamos a investigar estos dos tipos de tareas con más detalle.

Creación de tareas Background-Safe

Algunas aplicaciones contienen tareas que iOS no debe interrumpir si la aplicación cambia el estado. Una manera de proteger estas tareas de ser interrumpidas es registrarlas con iOS como tareas de larga duración. Puede usar este patrón en cualquier parte de la aplicación donde no quiera que se interrumpa una tarea si el usuario coloca la aplicación en segundo plano. Un excelente candidato para este patrón serían tareas como enviar información de registro de un nuevo usuario al servidor o comprobar la información de inicio de sesión.

El siguiente fragmento de código muestra el registro de una tarea que se va a ejecutar en segundo plano:

nint taskID = UIApplication.SharedApplication.BeginBackgroundTask( () => {});

//runs on main or background thread
FinishLongRunningTask(taskID);

UIApplication.SharedApplication.EndBackgroundTask(taskID);

El proceso de registro empareja una tarea con un identificador único, taskID, y, a continuación, lo ajusta en las llamadas BeginBackgroundTask y EndBackgroundTask coincidentes. Para generar el identificador, realizamos una llamada al método BeginBackgroundTask en el objeto UIApplication y, a continuación, iniciamos la tarea de ejecución prolongada, normalmente en un nuevo subproceso. Una vez completada la tarea, llamamos a EndBackgroundTask y pasamos el mismo identificador. Esto es importante porque iOS finalizará la aplicación si una llamada BeginBackgroundTask no tiene una EndBackgroundTask coincidente.

Importante

Las tareas Background-safe se pueden ejecutar en el subproceso principal o en un subproceso en segundo plano, en función de las necesidades de la aplicación.

Realizar tareas durante DidEnterBackground

Además de hacer que una tarea de ejecución prolongada sea segura, el registro se puede usar para iniciar tareas a medida que una aplicación se coloca en segundo plano. iOS proporciona un método de evento en la clase AppDelegate denominada DidEnterBackground que se puede usar para guardar el estado de la aplicación, guardar los datos del usuario y cifrar contenido confidencial antes de que una aplicación entre en segundo plano. Una aplicación tiene aproximadamente cinco segundos para volver de este método o se finalizará. Por lo tanto, se puede llamar a las tareas de limpieza que pueden tardar más de cinco segundos en completarse desde el método DidEnterBackground. Estas tareas se deben invocar en un subproceso independiente.

El proceso es casi idéntico al de registrar una tarea de ejecución prolongada. El siguiente fragmento de código ilustra esto en acción:

public override void DidEnterBackground (UIApplication application) {
  nint taskID = UIApplication.SharedApplication.BeginBackgroundTask( () => {});
  new Task ( () => {
    DoWork();
    UIApplication.SharedApplication.EndBackgroundTask(taskID);
  }).Start();
}

Comenzamos reemplazando el método DidEnterBackground en AppDelegate, donde registramos nuestra tarea a través de BeginBackgroundTask como hicimos en el ejemplo anterior. A continuación, generamos un nuevo subproceso y realizamos nuestra tarea de larga duración. Tenga en cuenta que la llamada EndBackgroundTask ahora se realiza desde dentro de la tarea de ejecución prolongada, ya que el método DidEnterBackground ya se habrá devuelto.

Importante

iOS usa un mecanismo guardián para asegurarse de que la interfaz de usuario de una aplicación sigue respondiendo. Una aplicación que pasa demasiado tiempo en DidEnterBackground dejará de responder en la interfaz de usuario. Iniciar tareas para ejecutarse en segundo plano permite que DidEnterBackground vuelva de forma oportuna, manteniendo la interfaz de usuario con capacidad de respuesta y evitando que el guardián detenga la aplicación.

Control de los límites de tiempo de tarea en segundo plano

iOS coloca límites estrictos sobre cuánto tiempo se puede ejecutar una tarea en segundo plano y, si la llamada EndBackgroundTask no se realiza dentro del tiempo asignado, se finalizará la aplicación. Al realizar un seguimiento del tiempo de segundo plano restante y usar controladores de expiración cuando sea necesario, podemos evitar que iOS termine la aplicación.

Acceso al tiempo en segundo plano restante

Si una aplicación con tareas registradas se mueve a segundo plano, las tareas registradas obtendrán unos 600 segundos de ejecución. Podemos comprobar cuánto tiempo tiene que completar la tarea mediante la propiedad estática BackgroundTimeRemaining de la clase UIApplication. El código siguiente nos dará el tiempo, en segundos, que nuestra tarea en segundo plano tiene:

double timeRemaining = UIApplication.SharedApplication.BackgroundTimeRemaining;

Evitar la terminación de la aplicación con controladores de expiración

Además de conceder acceso a la propiedad BackgroundTimeRemaining, iOS proporciona una manera correcta de controlar la expiración del tiempo en segundo plano a través de un controlador de expiración. Se trata de un bloque opcional de código que se ejecutará cuando el tiempo asignado para una tarea esté a punto de expirar. El código del controlador de expiración llama a EndBackgroundTask y pasa el identificador de tarea, lo que indica que la aplicación se comporta correctamente y evita que iOS termine la aplicación incluso si a la tarea se le agota el tiempo. Debe llamarse a EndBackgroundTask dentro del controlador de expiración, así como en el curso normal de ejecución.

El controlador de expiración se expresa como una función anónima mediante una expresión lambda, como se muestra a continuación:

Task.Factory.StartNew( () => {

    //expirationHandler only called if background time allowed exceeded
    var taskId = UIApplication.SharedApplication.BeginBackgroundTask(() => {
        Console.WriteLine("Exhausted time");
        UIApplication.SharedApplication.EndBackgroundTask(taskId); 
    });
    while(myFlag == true)
    {
        Console.WriteLine(UIApplication.SharedApplication.BackgroundTimeRemaining);
        myFlag = SomeCalculationNeedsMoreTime();
    }
    //Only called if loop terminated due to myFlag and not expiration of time
    UIApplication.SharedApplication.EndBackgroundTask(taskId);
});

Aunque los controladores de expiración no son necesarios para que el código se ejecute, siempre debe usar un controlador de expiración con una tarea en segundo plano.

Tareas en segundo plano en iOS 7+

El mayor cambio en iOS 7 con respecto a las tareas en segundo plano no es cómo se implementan las tareas, pero cuando se ejecutan.

Recuerde que antes de iOS 7, una tarea que se ejecuta en segundo plano tenía 600 segundos para completarse. Una razón para este límite es que una tarea que se ejecuta en segundo plano mantendrá el dispositivo activo durante la duración de la tarea:

Graph of the task keeping the app awake pre-iOS 7

El procesamiento en segundo plano de iOS 7 está optimizado para una mayor duración de la batería. En iOS 7, las tareas en segundo plano son oportunistas: en lugar de mantener el dispositivo activo, las tareas respetan cuándo el dispositivo va a dormir y, en su lugar, realizan su procesamiento en fragmentos cuando el dispositivo se reactiva para controlar las llamadas telefónicas, las notificaciones, los correos electrónicos entrantes y otras interrupciones comunes. En el diagrama siguiente se proporciona información sobre cómo se puede dividir una tarea:

Graph of the task being broken into chunks post-iOS 7

Dado que el tiempo de ejecución de la tarea no es más continuo, las tareas que realizan transferencias de red deben controlarse de forma diferente en iOS 7. Se recomienda a los desarrolladores que usen la API NSURlSession para controlar las transferencias de red. La siguiente sección es una introducción a las transferencias en segundo plano.

Transferencias en segundo plano

La red troncal de las transferencias en segundo plano en iOS 7 es la nueva API NSURLSession. NSURLSession nos permite crear tareas para:

  1. Transferir contenido a través de interrupciones de red y dispositivo.
  2. Cargar y descargar archivos grandes (servicio de transferencia en segundo plano).

Veamos cómo funciona esto.

API NSURLSession

NSURLSession es una API eficaz para transferir contenido a través de la red. Proporciona un conjunto de herramientas para controlar la transferencia de datos a través de interrupciones de red y cambios en los estados de la aplicación.

La API NSURLSession crea una o varias sesiones, que a su vez generan tareas para trasladar bloques de datos relacionados a través de la red. Las tareas se ejecutan de forma asincrónica para transferir datos de forma rápida y confiable. Dado que NSURLSession es asincrónica, cada sesión requiere un bloque de controlador de finalización para que el sistema y la aplicación sepan cuándo se completa una transferencia.

Para realizar una transferencia de red válida tanto en iOS 7 como después de iOS 7, compruebe si NSURLSession está disponible para poner en cola las transferencias y use una tarea en segundo plano normal para realizar la transferencia si no lo está:

if ([NSURLSession class]) {
  // Create a background session and enqueue transfers
}
else {
  // Start a background task and transfer directly
  // Do NOT make calls to update the UI here!
}

Importante

Evite realizar llamadas para actualizar la interfaz de usuario desde segundo plano en código compatible con iOS 6, ya que iOS 6 no admite actualizaciones de interfaz de usuario en segundo plano y finalizará la aplicación.

La API NSURLSession incluye un amplio conjunto de características para controlar la autenticación, administrar las transferencias con errores y notificar errores del lado cliente, pero no del lado servidor. Ayuda a mitigar las interrupciones en el tiempo de ejecución de tareas introducidas en iOS 7 y también proporciona compatibilidad para transferir archivos grandes de forma rápida y confiable. En la sección siguiente se explora esta segunda característica.

Servicio de transferencia en segundo plano

Antes de iOS 7, la carga o descarga de archivos en segundo plano no era confiable. Las tareas en segundo plano obtienen un tiempo limitado para ejecutarse, pero el tiempo necesario para transferir un archivo varía con la red y el tamaño del archivo. En iOS 7, podemos usar NSURLSession para cargar y descargar archivos grandes correctamente. El tipo de sesión de NSURLSession particular que controla las transferencias de red de archivos grandes en segundo plano se conoce como servicio de transferencia en segundo plano.

Las transferencias iniciadas mediante el servicio de transferencia en segundo plano se administran mediante el sistema operativo y proporcionan API para controlar la autenticación y los errores. Dado que las transferencias no están limitadas por un límite de tiempo arbitrario, se pueden usar para cargar o descargar archivos grandes, actualizar automáticamente el contenido en segundo plano, etc. Consulte el Tutorial de transferencia en segundo plano para obtener más información sobre cómo implementar el servicio.

El servicio de transferencia en segundo plano se empareja a menudo con la captura en segundo plano o notificaciones remotas para ayudar a las aplicaciones a actualizar el contenido en segundo plano. En las dos secciones siguientes, presentamos el concepto de registrar aplicaciones completas para que se ejecuten en segundo plano en iOS 6 e iOS 7.