JobScheduler: control de la demora desde que se cumplen las restricciones hasta que se ejecuta el trabajo

6 minutos de lectura

avatar de usuario de drmrbrewer
cervecero

Estoy usando JobScheduler para programar trabajos. Principalmente lo estoy usando para el .setRequiredNetworkType() método, que le permite especificar que solo desea que el trabajo se programe cuando se establezca una conexión de red (o más específicamente, una conexión no medida).

Estoy usando el siguiente código bastante sencillo para programar mis trabajos:

PersistableBundle extras = new PersistableBundle();
extras.putInt("anExtraInt", someInt);
int networkConstraint = useUnmetered ? JobInfo.NETWORK_TYPE_UNMETERED : JobInfo.NETWORK_TYPE_ANY;

ComponentName componentName = new ComponentName(context, MyJobService.class);
JobInfo jobInfo = new JobInfo.Builder(jobId, componentName)
        .setRequiredNetworkType(networkConstraint)
        .setExtras(extras)
        .build();

JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
jobScheduler.schedule(jobInfo);

Por lo tanto, solo se impone una restricción a la programación: una conexión de red (que puede ser ‘cualquiera’ o ‘no medida’).

Versión corta de la pregunta.

¿Cómo especifico un retraso máximo desde el cumplimiento de todas las restricciones y la ejecución real del trabajo, por ejemplo, “ejecutar el trabajo dentro de los 2 segundos posteriores a la existencia de una conexión de red”?

Versión más larga (con divagaciones)

El problema

Lo que encuentro es que en algunos dispositivos, si un trabajo está programado durante un período en el que la restricción de la red es ya satisfechoel trabajo se ejecutará inmediatamente (o lo suficientemente rápido como para que el usuario lo perciba).

Pero en otros dispositivos, incluso si ya hay disponible una conexión de red adecuada (para que el trabajo pueda ejecutarse de inmediato), hay un retraso significativo antes de que realmente se ejecute. Entonces, si esto es en respuesta a una acción del usuario, la impresión es que no ha pasado nada y que la aplicación está rota.

Ahora, soy muy consciente de que esta es probablemente la intención con JobScheduler… que depende del sistema programar el trabajo para que se adapte mejor a otras demandas, y que no hay garantía de que el trabajo se ejecute inmediatamente cuando se cumplan todas las restricciones.

Pero sería bueno poder tener alguno control sobre él, cuando sea necesario. Por lo tanto, para los trabajos que se realizan según un cronograma, sin la participación del usuario, dar al sistema un control total sobre la sincronización precisa está bien y es bueno.

Pero donde el trabajo es en respuesta a una acción del usuario, quiero que el trabajo se ejecute sin demora… suponiendo que la conexión de red esté allí. (Si no hay conexión, se puede mostrar un mensaje de que la acción ocurrirá cuando se restablezca una conexión de red, y el JobScheduler luego se encarga de garantizar que el trabajo se ejecute cuando se restablezca la red).

setOverrideDeadline() no es una solución?

Puedo ver eso JobInfo.Builder tiene un setOverrideDeadline() método, que es casi lo que quiero. Pero eso especifica el retraso máximo. desde que se programa el trabajo (es decir, ejecutar el trabajo en 10 segundos incluso si no se cumplen todas las restricciones), y no desde que se han satisfecho todas las restricciones (es decir, ejecute el trabajo dentro de los 10 segundos de haber satisfecho todas las restricciones).

EDITAR: y parece haber un error molesto que puede hacer que el trabajo se ejecute dos veces cuando se usa setOverrideDeadline(): ver aquí y aquí.

¿Qué pasa con Firebase JobDispatcher?

veo que Firebase JobDispatcher tiene un Trigger.NOW desencadenar (“significa que el trabajo debe ejecutarse tan pronto como se cumplan sus restricciones de tiempo de ejecución”). Tal vez ese es el camino a seguir si JobSchedulerno es compatible con esto de forma nativa? Firebase JobDispatcher me ha desanimado porque parece que está usando un mazo para romper una nuez… y parece que Firebase tiene que ver con la mensajería en la nube, etc., que está muy lejos de la programación de tareas locales (que debería ser un preocupación enteramente local). Y parece requerir los servicios de Google Play, lo que nuevamente parece completamente innecesario para la programación de tareas locales. Además, si la activación inmediata es posible con Firebase, y Firebase solo usa JobScheduler para Android L+, entonces seguramente debe ser posible hacer esto directamente con JobScheduler sin depender de Firebase?

EDITAR: ahora he probado esto, e incluso Trigger.NOW no garantiza una respuesta inmediata… de hecho, me doy cuenta de que hay un retraso de casi exactamente 30 segundos en mi dispositivo, lo cual es extraño.

Fallando en eso…

En la actualidad, la única forma que veo para garantizar la ejecución inmediata (si se cumplen las restricciones) es no usar JobScheduler.

O tal vez verifique manualmente las restricciones iniciales y ejecute el trabajo con un setOverrideDeadline() de 0 si se cumplen todas las restricciones; de lo contrario, ejecútelo sin setOverrideDeadline().

Parecería mucho más preferible tener la capacidad de controlar el tiempo de JobScheduler mismo, un poco como usted puede con el setWindow() método de AlarmManager.

  • @OP, ¿podría organizar su publicación en subcategorías más pequeñas sobre lo que está tratando de decir con respecto al problema que está tratando de solucionar?

    – Jox Traex

    8 sep 2016 a las 8:00


  • @JoxTraex Ya había intentado eso (versión corta frente a versión larga) pero ahora he agregado más subestructura. Nadie necesita leer más allá de la versión corta, pero la versión larga está ahí para obtener más información.

    – drmrbrewer

    8 de septiembre de 2016 a las 8:42

  • Como sugirió, sospecho que esta falta de API es por diseño. Sin duda, puede solicitarlo como una mejora de funciones, aunque dicha mejora no aparecerá hasta Android O en el mejor de los casos (presumiblemente), lo que significa que será 2022 antes de que dicha función se vuelva común. “tal vez haga la verificación de restricciones inicial manualmente y ejecute el trabajo con un setOverrideDeadline() de 0 si se cumplen todas las restricciones, de lo contrario, ejecútelo sin setOverrideDeadline()” — o simplemente haga el trabajo si se cumplen las restricciones y omita el trabajo. No tiene sentido usar JobScheduler para trabajo inmediato.

    – CommonsWare

    10/09/2016 a las 20:17

  • Entonces @CommonsWare si tengo un JobService configurado para llevar a cabo el trabajo programado, ¿hay alguna forma de ejecutarlo directamente, no a través de un JobSchedulersi quiero que solo corra ahora? Odio la duplicación de código, y para crear una separada Service porque esto (incluso con funciones compartidas) parece poco elegante…. mucho más ordenado solo para comenzar el JobService ¿directamente?

    – drmrbrewer

    10/09/2016 a las 21:08

  • “Si quiero que solo funcione ahora?” — AFAIK, puedes llamar startService() por tu cuenta JobService. Refactorice el trabajo real a realizar en una clase separada (para su subproceso de fondo), una que se puede crear y usar desde onStartJob() o de startService(). No parece que puedas crear un JobParameters usted mismo, por lo que no puede tener startService() llamar onStartJob()a menos que hayas pasado null Para el JobParameters.

    – CommonsWare

    10/09/2016 a las 21:12

Un programador de trabajos es para programar trabajos: desencadenando periódicamente, con retraso o con restricciones a otros trabajos. Si desea iniciar un trabajo al instante, no es necesario programarlo, simplemente inícielo.

ConnectivityManager cm =
            (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo networkInfo = cm.getActiveNetworkInfo();
    if (networkInfo != null && networkInfo.isConnectedOrConnecting()) {

        // If there is connectivity, Launch the job directly here

    } else {

        PersistableBundle extras = new PersistableBundle();
        extras.putInt("anExtraInt", someInt);
        int networkConstraint = useUnmetered ?       
        JobInfo.NETWORK_TYPE_UNMETERED : JobInfo.NETWORK_TYPE_ANY;

        ComponentName componentName = new ComponentName(context,MyJobService.class);
        JobInfo jobInfo = new JobInfo.Builder(jobId, componentName)
                .setRequiredNetworkType(networkConstraint)
                .setExtras(extras)
                .build();

        JobScheduler jobScheduler = (JobScheduler)      context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
        jobScheduler.schedule(jobInfo);
    }

¿Ha sido útil esta solución?