Compartir vía


Transferencia en segundo plano y NSURLSession en Xamarin.iOS

Una transferencia en segundo plano se inicia mediante la configuración de un elemento NSURLSession en segundo plano y la puesta en cola de tareas de carga o descarga. Si las tareas se completan mientras la aplicación está en segundo plano, suspendida o terminada, iOS llamará al controlador de finalización en AppDelegate de la aplicación para notificárselo. En el diagrama siguiente se muestra esto en acción:

Una transferencia en segundo plano se inicia mediante la configuración de una NSURLSession en segundo plano y la puesta en cola de tareas de carga o descarga

Configuración de una sesión en segundo plano

Para crear una sesión en segundo plano, cree un nuevo elemento NSUrlSession y configúrelo mediante un objeto NSUrlSessionConfiguration.

El objeto de configuración determina qué puede hacer la sesión y los tipos de tareas que puede ejecutar. Las sesiones configuradas mediante el método CreateBackgroundSessionConfiguration se ejecutarán en un proceso independiente y realizarán transferencias discrecionales (Wi-Fi) para conservar los datos y la duración de la batería. En el ejemplo de código siguiente se muestra la configuración adecuada de una sesión de transferencia en segundo plano mediante el método CreateBackgroundSessionConfiguration y un identificador de cadena único:

public partial class SimpleBackgroundTransferViewController : UIViewController
{
  NSUrlSession session = null;

  NSUrlSessionConfiguration configuration =
      NSUrlSessionConfiguration.CreateBackgroundSessionConfiguration ("com.SimpleBackgroundTransfer.BackgroundSession");
  session = NSUrlSession.FromConfiguration
      (configuration, new MySessionDelegate(), new NSOperationQueue());

}

Además de un objeto de configuración, una sesión también requiere un delegado de sesión y una cola. La cola determina el orden en el que se completarán las tareas. El delegado de sesión acompaña al proceso de transferencia y controla la autenticación, el almacenamiento en caché y otros problemas relacionados con la sesión.

Uso de tareas y delegados

Ahora que hemos configurado una sesión en segundo plano, vamos a iniciar las tareas para controlar la transferencia. Podemos realizar un seguimiento de estas tareas mediante una instancia NSUrlSessionDelegate denominada delegado de sesión. El delegado de sesión es responsable de activar una aplicación terminada o suspendida en segundo plano para controlar la autenticación, los errores o la finalización de la transferencia.

Un objeto NSUrlSessionDelegate proporciona los siguientes métodos básicos para comprobar el estado de transferencia:

  • DidFinishEventsForBackgroundSession: se llama a este método cuando se han completado todas las tareas y finaliza la transferencia.
  • DidReceiveChallenge: se llama a este método para solicitar credenciales cuando se requiere autorización.
  • DidBecomeInvalidWithError: se llama a este método si NSURLSession se invalida.

Las sesiones en segundo plano requieren delegados más especializados en función de los tipos de tareas que se ejecutan. Las sesiones en segundo plano se limitan a dos tipos de tareas:

  • Tareas de carga: las tareas de tipo NSUrlSessionUploadTask usan la interfaz INSUrlSessionTaskDelegate, que implementa INSUrlSessionDelegate. Esto proporciona métodos adicionales para realizar un seguimiento del progreso de la carga, controlar el redireccionamiento HTTP, etc.
  • Tareas de descarga: las tareas de tipo NSUrlSessionDownloadTask usan la interfaz INSUrlSessionDownloadDelegate, que implementa INSUrlSessionDelegate y INSUrlSessionTaskDelegate. Esto proporciona todos los métodos de las tareas de carga, así como métodos específicos de descarga para realizar un seguimiento del progreso de la descarga y determinar cuándo se ha reanudado o completado una tarea de descarga.

El código siguiente define una tarea que se puede usar para descargar una imagen de una dirección URL. La tarea se inicia llamando a CreateDownloadTask en la sesión en segundo plano y pasando la solicitud de dirección URL:

const string DownloadURLString = "http://xamarin.com/images/xamarin.png"; // or other hosted file
public NSUrlSessionDownloadTask downloadTask;

NSUrl downloadURL = NSUrl.FromString (DownloadURLString);
NSUrlRequest request = NSUrlRequest.FromUrl (downloadURL);
downloadTask = session.CreateDownloadTask (request);

A continuación, cree un nuevo delegado de descarga de sesión para realizar un seguimiento de todas las tareas de descarga de esta sesión. La clase de delegado debe heredar de NSObject e implementar la interfaz necesaria:

public class MySessionDelegate : NSObject, INSUrlSessionDownloadDelegate
{
  public void DidWriteData (NSUrlSession session, NSUrlSessionDownloadTask downloadTask, long bytesWritten, long totalBytesWritten, long totalBytesExpectedToWrite)
  {
    Console.WriteLine (string.Format ("DownloadTask: {0}  progress: {1}", downloadTask, progress));
    InvokeOnMainThread( () => {
      // update UI with progress bar, if desired
    });
  }
  ...
}

Para averiguar el progreso de una tarea de descarga, invalide el método DidWriteData para realizar un seguimiento del progreso e incluso actualizar la interfaz de usuario. Las actualizaciones de la interfaz de usuario aparecerán inmediatamente si la aplicación está en primer plano o esperará al usuario la próxima vez que abra la aplicación.

La API de delegado de sesión proporciona un amplio kit de herramientas para interactuar con tareas. Para obtener una lista completa de los métodos de delegado de sesión, consulte la documentación de la API NSUrlSessionDelegate.

Importante

Las sesiones en segundo plano se inician en un subproceso en segundo plano, por lo que las llamadas para actualizar la interfaz de usuario deben ejecutarse explícitamente en el subproceso de la interfaz de usuario llamando a InvokeOnMainThread para evitar que iOS termine la aplicación.

Control de la finalización de la transferencia

El último paso es informar a la aplicación cuando se hayan completado todas las tareas asociadas a la sesión y controlar el nuevo contenido.

En AppDelegate, suscríbase al evento HandleEventsForBackgroundUrl. Cuando la aplicación entra en segundo plano y se ejecuta una sesión de transferencia, se llama a este método y el sistema nos pasa un controlador de finalización:

public System.Action backgroundSessionCompletionHandler;

public void HandleEventsForBackgroundUrl (UIApplication application, string sessionIdentifier, System.Action completionHandler)
{
  this.backgroundSessionCompletionHandler = completionHandler;
}

Use el controlador de finalización para informar a iOS cuando la aplicación haya terminado de procesarse.

Recuerde que una sesión puede generar varias tareas para procesar una transferencia. Cuando se completa la última tarea, se vuelve a iniciar una aplicación suspendida o terminada en segundo plano. A continuación, la aplicación vuelve a conectarse a NSURLSession mediante el identificador de sesión único y llama a DidFinishEventsForBackgroundSession en el delegado de sesión. Este método es la oportunidad de la aplicación de controlar nuevo contenido, incluida la actualización de la interfaz de usuario para reflejar los resultados de la transferencia:

public void DidFinishEventsForBackgroundSession (NSUrlSession session) {
  // Handle new information, update UI, etc.
}

Una vez que haya terminado de controlar el nuevo contenido, llame al controlador de finalización para informar al sistema de que es seguro tomar una instantánea de la aplicación y volver a suspensión:

public void DidFinishEventsForBackgroundSession (NSUrlSession session) {
  var appDelegate = UIApplication.SharedApplication.Delegate as AppDelegate;

  // Handle new information, update UI, etc.

  // call completion handler when you're done
  if (appDelegate.backgroundSessionCompletionHandler != null) {
    NSAction handler = appDelegate.backgroundSessionCompletionHandler;
    appDelegate.backgroundSessionCompletionHandler = null;
    handler.Invoke ();
  }
}

En este tutorial se describen los pasos básicos para implementar el servicio de transferencia en segundo plano en iOS 7 y versiones más recientes.