Bienvenido, me llamo Miguel y para hoy les traigo la cuarta publicación de nuestra serie WorkManager
.
Índice
1. Configuración Personalizada de WorkManager
De forma predeterminada, WorkManager
se configura automáticamente cuando se inicia su aplicación, utilizando opciones razonables que son adecuadas para la mayoría de las aplicaciones.
Si necesita más control sobre cómo WorkManager
gestiona y programa el trabajo, puede personalizar la configuración de WorkManager
inicializando WorkManager
usted mismo.
WorkManager 2.1.0
y posterior
WorkManager 2.1.0
tiene varias formas de configurar WorkManager
. La forma más flexible de proporcionar una inicialización personalizada para WorkManager
es utilizar inicialización bajo demanda, disponible en WorkManager 2.1.0
y posterior.
Las otras opciones son discutidas después.
La inicialización bajo demanda le permite crear WorkManager solo cuando ese componente es necesario, en lugar de cada vez que se inicia la aplicación.
Hacerlo saca a WorkManager de su ruta de inicio crítica, mejorando el rendimiento de inicio de la aplicación. Para utilizar la inicialización bajo demanda:
Para proporcionar su propia configuración, primero debe eliminar el inicializador predeterminado. Para hacerlo, actualice AndroidManifest.xml
usando la regla de combinación tools:node="remove"
:
<provider android:name="androidx.work.impl.WorkManagerInitializer" android:authorities="$applicationId.workmanager-init" tools:node="remove" />
Para obtener más información sobre el uso de reglas de combinación en su manifiesto, consulte la documentación en fusionando varios archivos de manifiesto.
Ten tu clase Application
implementar la interfaz Configuration.Provider
y proporcionar su propia implementación de Configuration.Provider.getWorkManagerConfiguration()
.
Cuando necesite usar WorkManager
, asegúrese de llamar al método WorkManager.getInstance(Context)
. WorkManager
llama a su aplicación personalizada método getWorkManagerConfiguration()
para descubrir su Configuration
. (No necesitas llamar WorkManager.initialize()
usted mismo.)
A continuación, se muestra un ejemplo de una implementación getWorkManagerConfiguration()
:
class MyApplication() : Application(), Configuration.Provider { override fun getWorkManagerConfiguration() = Configuration.Builder() .setMinimumLoggingLevel(android.util.Log.INFO) .build() }
WorkManager 2.0.1
y versiones anteriores
Para versiones anteriores de WorkManager
, hay dos opciones de inicialización:
En la mayoría de los casos, la inicialización predeterminada es todo lo que necesita.
Para un control más preciso de WorkManager
, puede especificar su propia configuración.
WorkManager
utiliza un ContentProvider
para inicializarse cuando se inicia la aplicación. Este código vive en la clase interna. androidx.work.impl.WorkManagerInitialize
y utiliza el valor predeterminado Configuration
.
El inicializador predeterminado se utiliza automáticamente a menos que deshabilitarlo explícitamente. El inicializador predeterminado es adecuado para la mayoría de las aplicaciones.
Si desea controlar el proceso de inicialización, debe deshabilitar el inicializador predeterminado, luego defina su propia configuración personalizada.
Una vez que se elimina el inicializador predeterminado, puede inicializar WorkManager manualmente:
// provide custom configuration val myConfig = Configuration.Builder() .setMinimumLoggingLevel(android.util.Log.INFO) .build() // initialize WorkManager WorkManager.initialize(this, myConfig)
El WorkManager
único. Asegúrese de que la inicialización se ejecute en Application.onCreate()
o en un ContentProvider.onCreate()
.
2. Work Manager Threading con Kotlin
Cuando usas un Worker
, WorkManager
llama automáticamente Worker.doWork()
en un hilo de fondo. El hilo de fondo proviene del Executor
especificado en WorkManager Configuration
.
De forma predeterminada, WorkManager
configura un Executor
para usted, pero también puede personalizar el suyo.
Por ejemplo, puede compartir un fondo existente Executor
en su aplicación, o cree un subproceso único Executor
para asegurarse de que todo su trabajo en segundo plano se ejecute en serie, o incluso especificar un ThreadPool
con un número de hilos diferente.
Para personalizar el, asegúrese de haber habilitado inicialización manual de WorkManager. Al configurar WorkManager
, puede especificar su Executor
como sigue:
WorkManager.initialize( context, Configuration.Builder() .setExecutor(Executors.newFixedThreadPool(8)) .build())
A continuación, se muestra un ejemplo de un trabajador simple que descarga el contenido de algunos sitios web de forma secuencial:
class DownloadWorker(context: Context, params: WorkerParameters) : Worker(context, params) { override fun doWork(): ListenableWorker.Result { for (i in 0..99) { try { downloadSynchronously("https://www.google.com") } catch (e: IOException) { return ListenableWorker.Result.failure() } } return ListenableWorker.Result.success() } }
Cuando un Worker
es detenido por cualquier motivo, recibe una llamada a Worker.onStopped()
. Anule este método o llame Worker.isStopped()
para verificar su código y liberar recursos cuando sea necesario.
Cuando Worker
en el ejemplo anterior está detenido, puede estar en medio de su ciclo de descarga de elementos y continuará haciéndolo aunque se haya detenido. Para optimizar este comportamiento, puede hacer algo como esto:
class DownloadWorker(context: Context, params: WorkerParameters) : Worker(context, params) { override fun doWork(): ListenableWorker.Result { for (i in 0..99) { if (isStopped) { break } try { downloadSynchronously("https://www.google.com") } catch (e: IOException) { return ListenableWorker.Result.failure() } } return ListenableWorker.Result.success() } }
Una vez Worker
ha sido detenido, no importa de donde vuelvas Worker.doWork()
; los Result
será ignorado.
Para los usuarios de Kotlin, WorkManager
proporciona un soporte de primera clase para corrutinas. Para comenzar, incluya work-runtime-ktx
en tu archivo gradle.
En lugar de extender Worke
, debe extender, que tiene una API ligeramente diferente. Por ejemplo, si quisiera construir un sencillo CoroutineWorker
para realizar algunas operaciones de red, haría lo siguiente:
class CoroutineDownloadWorker(context: Context, params: WorkerParameters) : CoroutineWorker(context, params) { override suspend fun doWork(): Result = coroutineScope { val jobs = (0 until 100).map { async { downloadSynchronously("https://www.google.com") } } // awaitAll will throw an exception if a download fails, which CoroutineWorker will treat as a failure jobs.awaitAll() Result.success() } }
class CoroutineDownloadWorker(context: Context, params: WorkerParameters) : CoroutineWorker(context, params) { override val coroutineContext = Dispatchers.IO override suspend fun doWork(): Result = coroutineScope { val jobs = (0 until 100).map { async { downloadSynchronously("https://www.google.com") } } // awaitAll will throw an exception if a download fails, which CoroutineWorker will treat as a failure jobs.awaitAll() Result.success() }}
CoroutineWorkers
manejan las paradas automáticamente cancelando la corrutina y propagando las señales de cancelación. No necesitas hacer nada especial para manejar work stoppages
.
Proporcionamos interoperabilidad entre WorkManager
y RxJava2
. Para comenzar, incluya work-rxjava2
la dependencia además de work-runtime
en su archivo gradle.
Entonces, en lugar de extender Worker
, deberías extender RxWorker
. Finalmente anule el método RxWorker.createWork()
para devolver un Single<Result>
indicando el Result
de su ejecución, de la siguiente manera:
public class RxDownloadWorker extends RxWorker { public RxDownloadWorker(Context context, WorkerParameters params) { super(context, params); } @Override public Single<Result> createWork() { return Observable.range(0, 100) .flatMap { download("https://www.google.com") } .toList() .map { Result.success() }; } }
Tenga en cuenta que RxWorker.createWork()
se llama en el hilo principal, pero el valor de retorno está suscrito a un hilo en segundo plano por defecto.
Puedes anular RxWorker.getBackgroundScheduler()
para cambiar el hilo de suscripción.
Detener un RxWorker
se deshará de la Observers
correctamente, por lo que no necesita manejar paros laborales de alguna manera especial.
En determinadas situaciones, es posible que deba proporcionar una estrategia de subprocesos personalizada. Por ejemplo, es posible que deba manejar una operación asincrónica basada en devolución de llamada.
En este caso, no puede confiar simplemente en un Worker
porque no puede hacer el trabajo bloqueando. WorkManager
admite este caso de uso con ListenableWorker
.
ListenableWorker
es la API de trabajador de nivel más bajo; Worker
, CoroutineWorker
y RxWorker
todos derivan de esta clase. Un ListenableWorker
solo indica cuándo el trabajo debe comenzar y detenerse y deja el enhebrado completamente en sus manos.
La señal de inicio de trabajo se invoca en el hilo principal, por lo que es muy importante que vaya a un hilo en segundo plano de su elección manualmente.
El método abstracto ListenableWorker.startWork()
devuelve un ListenableFuture
del Result
. Un ListenableFuture
es una interfaz ligera: es una Future
que proporciona funcionalidad para adjuntar oyentes y propagar excepciones.
En el método startWork
, se espera que devuelva un ListenableFuture
, que establecerá con el Result
de la operación una vez finalizada. Puedes crear ListenableFutures
de dos formas:
- Si usa Guayaba, use
ListeningExecutorService
. - De lo contrario, incluya
councurrent-futures
en tu archivogradle
y usaCallbackToFutureAdapter
.
Si quisiera ejecutar algún trabajo basado en una devolución de llamada asincrónica, haría algo como esto:
public class CallbackWorker extends ListenableWorker { public CallbackWorker(Context context, WorkerParameters params) { super(context, params); } @NonNull @Override public ListenableFuture<Result> startWork() { return CallbackToFutureAdapter.getFuture(completer -> { Callback callback = new Callback() { int successes = 0; @Override public void onFailure(Call call, IOException e) { completer.setException(e); } @Override public void onResponse(Call call, Response response) { ++successes; if (successes == 100) { completer.set(Result.success()); } } }; for (int i = 0; i < 100; ++i) { downloadAsynchronously("https://www.google.com", callback); } return callback; }); } }
¿Qué pasa si tu trabajo es detenido? Un ListenableWorker
es ListenableFuture
siempre se cancela cuando se espera que el trabajo se detenga.
Usando un CallbackToFutureAdapter
, simplemente tiene que agregar un oyente de cancelación, de la siguiente manera:
public class CallbackWorker extends ListenableWorker { public CallbackWorker(Context context, WorkerParameters params) { super(context, params); } @NonNull @Override public ListenableFuture<Result> startWork() { return CallbackToFutureAdapter.getFuture(completer -> { Callback callback = new Callback() { int successes = 0; @Override public void onFailure(Call call, IOException e) { completer.setException(e); } @Override public void onResponse(Call call, Response response) { ++successes; if (successes == 100) { completer.set(Result.success()); } } }; completer.addCancellationListener(cancelDownloadsRunnable, executor); for (int i = 0; i < 100; ++i) { downloadAsynchronously("https://www.google.com", callback); } return callback; }); } }
Gracias por leer este artículo.
Añadir comentario