Migración de GCMNetworkManager a WorkManager

En este documento, se explica cómo migrar apps para usar la biblioteca cliente de WorkManager en lugar de la biblioteca de GCMNetworkManager con el fin de realizar operaciones en segundo plano. Por lo general, la forma preferida para hacer que una app programe trabajos en segundo plano es mediante WorkManager. Al incluir también la biblioteca de WorkManager GCM, puedes habilitar WorkManager para usar GCM para programar las tareas cuando se ejecuta en dispositivos Android con una API nivel 22 o inferior.

Cómo migrar a WorkManager

Si tu app actualmente usa GCMNetworkManager para realizar operaciones en segundo plano, sigue estos pasos para migrar a WorkManager.

En los siguientes pasos, asumimos que estás comenzando con el siguiente código de GCMNetworkManager, que define y programa tu tarea:

Kotlin

    val myTask = OneoffTask.Builder()
        // setService() says what class does the work
        .setService(MyUploadService::class.java)
        // Don't run the task unless device is charging
        .setRequiresCharging(true)
        // Run the task between 5 & 15 minutes from now
        .setExecutionWindow(5 * DateUtil.MINUTE_IN_SECONDS,
                15 * DateUtil.MINUTE_IN_SECONDS)
        // Define a unique tag for the task
        .setTag("test-upload")
        // ...finally, build the task and assign its value to myTask
        .build()
    GcmNetworkManager.getInstance(this).schedule(myTask)
    

Java

    // In GcmNetworkManager, this call defines the task and its
    // runtime constraints:
    OneoffTask myTask = new OneoffTask.Builder()
        // setService() says what class does the work
        .setService(MyUploadService.class)
        // Don't run the task unless device is charging
        .setRequiresCharging(true)
        // Run the task between 5 & 15 minutes from now
        .setExecutionWindow(
            5 * DateUtil.MINUTE_IN_SECONDS,
            15 * DateUtil.MINUTE_IN_SECONDS)
        // Define a unique tag for the task
        .setTag("test-upload")
        // ...finally, build the task and assign its value to myTask
        .build();
    GcmNetworkManager.getInstance(this).schedule(myTask);
    

En este ejemplo, asumimos que MyUploadService define la operación de carga real:

Kotlin

    class MyUploadService : GcmTaskService() {
        fun onRunTask(params: TaskParams): Int {
            // Do some upload work
            return GcmNetworkManager.RESULT_SUCCESS
        }
    }
    

Java

    class MyUploadService extends GcmTaskService {
        @Override
        public int onRunTask(TaskParams params) {
            // Do some upload work
            return GcmNetworkManager.RESULT_SUCCESS;
        }
    }
    

Cómo incluir las bibliotecas de WorkManager

Para usar las clases de WorkManager, debes agregar la biblioteca de WorkManager a tus dependencias de compilación. También debes agregar la biblioteca de WorkManager GCM, que permite a WorkManager usar GCM para la programación de trabajos cuando tu app se ejecuta en dispositivos que no son compatibles con JobScheduler (es decir, dispositivos con API nivel 22 o inferior). Para obtener información detallada sobre cómo agregar las bibliotecas, consulta las notas de la versión de WorkManager.

Para agregar las bibliotecas a tu app, agrega las siguientes dependencias a tu archivo build.gradle:

dependencies {
        def work_version = "2.2.0-beta02"

        // WorkManager library
        implementation "androidx.work:work-runtime:$work_version"

        // Include this library for Kotlin coroutines
        implementation "androidx.work:work-runtime-ktx:$work_version"

        // WorkManager GCM library
        implementation "androidx.work:work-gcm:$work_version"
      }
    

Cómo modificar tu manifiesto

Cuando implementaste GCMNetworkmanager, agregaste una instancia de GcmTaskService al manifiesto de tu app, como se describe en la documentación de referencia de GcmNetworkManager. GcmTaskService examina la tarea entrante y la delega al controlador de tareas. WorkManager administra la delegación de tareas a tu trabajador, por lo que ya no necesitas una clase que lo haga; simplemente puedes quitar tu GcmTaskService del manifiesto.

Cómo definir al trabajador

Tu implementación de GCMNetworkManager define una tarea OneoffTask o RecurringTask, que especifica exactamente qué trabajo debe hacerse. Debes volver a escribir eso como Worker, como se indica en Cómo definir tus solicitudes de trabajo.

En el código GCMNetworkManager de ejemplo, se define una tarea myTask. El equivalente de WorkManager se ve de la siguiente manera:

Kotlin

    class UploadWorker(context: Context, params: WorkerParameters)
                            : Worker(context, params) {
        override fun doWork() : Result {
            // Do the upload operation ...
            myUploadOperation()

            // Indicate whether the task finished successfully with the Result
            return Result.success()
        }
    }
    

Java

    public class UploadWorker extends Worker {

        public UploadWorker(
            @NonNull Context context,
            @NonNull WorkerParameters params) {
            super(context, params);
        }

        @Override
        public Result doWork() {
          // Do the upload operation ...

          myUploadOperation()

          // Indicate whether the task finished successfully with the Result
          return Result.success()
        }
    }
    

Existen las siguientes diferencias entre la tarea de GCM y Worker:

  • GCM usa un objeto TaskParams para pasar parámetros a la tarea. WorkManager usa datos de entrada, que puedes especificar en WorkRequest, como se describe en la documentación de WorkManager acerca de cómo definir entradas y salidas para tu tarea. En ambos casos, puedes pasar pares clave-valor que especifiquen los parámetros persistentes necesarios para la tarea.
  • El elemento GcmTaskService señala el éxito o el fracaso cuando se muestran indicadores como GcmNetworkManager.RESULT_SUCCESS. Un elemento Worker de WorkManager indica sus resultados. Para ello, usa un método ListenableWorker.Result, como ListenableWorker.Result.success(), y muestra el valor de retorno de ese método.
  • Como mencionamos, cuando defines el elemento Worker, no estableces restricciones ni etiquetas. Eso lo haces en el siguiente paso, cuando creas el elemento WorkRequest.

Cómo programar la solicitud de trabajo

La definición de Worker especifica qué necesitas hacer. Para especificar cuándo se debe realizar el trabajo, debes definir WorkRequest de la siguiente manera:

  1. Crea un elemento OneTimeWorkRequest o PeriodicWorkRequest, y establece las restricciones que desees para especificar cuándo se debe ejecutar la tarea y las etiquetas que identifiquen tu trabajo.
  2. Pasa la solicitud a WorkManager.enqueue() con el objetivo de que la tarea se ponga en cola para su ejecución.

Por ejemplo, en la sección anterior se mostró cómo convertir un elemento OneoffTask en un elemento Worker equivalente. Sin embargo, ese Worker no incluía las restricciones y la etiqueta de ejecución de OneoffTask. En cambio, estableceremos las restricciones y el ID de la tarea cuando creemos el elemento WorkRequest. También especificaremos que la tarea no debe ejecutarse a menos que haya una conexión de red. No es necesario que solicites explícitamente una conexión de red con GCMNetworkManager, ya que GCMNetworkManager requiere una conexión de red de forma predeterminada, pero WorkManager no requiere una conexión de red a menos que agregues específicamente esa restricción. Una vez que definimos el elemento WorkRequest, lo colocamos en cola con WorkManager.

Kotlin

    val uploadConstraints = Constraints.Builder()
        .setRequiredNetworkType(NetworkType.CONNECTED)
        .setRequiresCharging(true).build()

    val uploadTask = OneTimeWorkRequestBuilder<UploadWorker>()
        .setConstraints(uploadConstraints)
        .build()
    WorkManager.getInstance().enqueue(uploadTask)
    

Java

    Constraints uploadConstraints = new Constraints.Builder()
        .setRequiredNetworkType(NetworkType.CONNECTED)
        .setRequiresCharging(true)
        .build();

    OneTimeWorkRequest uploadTask =
            new OneTimeWorkRequest.Builder(UploadWorker.class)
      .setConstraints(uploadConstraints)
      .build();
    WorkManager.getInstance().enqueue(uploadTask);
    

Mapeados de API

En esta sección, se describe cómo se mapean algunas funciones y restricciones de GCMNetworkManager con sus equivalentes de WorkManager.

Mapeado de restricciones

GCMNetworkManager te permite establecer una serie de restricciones que se aplican en el momento de la ejecución de tu tarea. En la mayoría de los casos, existe una restricción equivalente de WorkManager clara. En esta sección, se enumeran esas equivalencias.

Llama al método adecuado en el objeto Builder de la tarea para establecer restricciones en las tareas de GCMNetworkManager. Por ejemplo, puedes establecer un requisito de red llamando a Task.Builder.setRequiredNetwork().

En WorkManager, puedes crear un objeto Constraints.Builder y llamar a los métodos de ese objeto para establecer restricciones (por ejemplo, Constraints.Builder.setRequiredNetworkType()),

y, luego, usar el objeto Builder para crear un objeto Constraints que puedes adjuntar a la solicitud de trabajo. Para obtener más información, consulta Cómo definir tus solicitudes de trabajo.

Restricción de GCMNetworkManager Equivalente de WorkManager Notas
setPersisted() (no se necesita) Todos los trabajos de WorkManager se conservan después de todos los reinicios del dispositivo.
setRequiredNetwork() setRequiredNetworkType() De manera predeterminada, GCMNetworkManager requiere acceso a la red. WorkManager no necesita acceso a la red de manera predeterminada. Si tu trabajo requiere acceso a la red, debes usar setRequiredNetworkType(CONNECTED) o establecer algún tipo de red más específico.
setRequiresCharging() requiresCharging()

Otros mapeos

Además de las restricciones, hay otras opciones de configuración que puedes aplicar a las tareas de GCMNetworkManager. En esta sección, se describe la forma correspondiente de aplicar esas opciones de configuración a un trabajo de WorkManager.

Etiquetas

Todas las tareas de GCMNetworkManager deben tener una string de etiqueta, que se establece llamando al método setTag() del objeto Builder. Los trabajos de WorkManager se identifican de forma exclusiva mediante un ID, que WorkManager genera automáticamente. Puedes obtener ese ID llamando a WorkRequest.getId(). Además, las solicitudes de trabajo pueden tener de forma opcional una o más etiquetas. Para establecer una etiqueta en tu trabajo de WorkManager, llama al método WorkRequest.Builder.addTag() antes de usar ese objeto Builder para crear el WorkRequest.

En GCMNetworkManager, puedes llamar a setUpdateCurrent() para especificar si la tarea debe reemplazar cualquier tarea existente con la misma etiqueta. El enfoque equivalente de WorkManager es poner en cola la tarea llamando a enqueueUniqueWork() o enqueueUniquePeriodicWork(); si usas estos métodos, le asignas un nombre único al trabajo y también especificas cómo WorkManager debe manejar la solicitud si ya hay un trabajo pendiente con ese nombre. Para obtener más información, consulta Cómo administrar el trabajo único.

Parámetros de la tarea

Para pasar parámetros a un trabajo de GCMNetworkManager, llama a Task.Builder.setExtras() y pásalos dentro de un objeto Bundle. WorkManager te permite pasar un objeto Data que contenga parámetros como pares clave-valor al trabajo de WorkManager. Si quieres obtener más información, consulta Cómo definir entradas y salidas para tu tarea.