Hola a todos
Después de ver que hay bastante foreros interesados en el tema, e incluso entolium me pidio hacer un tutorial sobre esto, abro este tutorial para intentar aclarar los conceptos mas generales de los Módulos de Servicios.
Si eres nuevo en B4A te recomiendo que antes leas la “Guia rápida del Ciclo de Vida de Android.” (http://www.b4x.com/android/forum/threads/b4a-tutorial-guia-rápida-del-ciclo-de-vida-de-android.38760/#post-229579) , así se entenderá mucho mejor como funciona y actúa un Servicio.
Cuando una aplicación no está visible su código está en pausa (Activity_Pause) . Por lo tanto utilizando solo el código de los Activity no será posible ejecutar ningún código. Esto es la base para entender por que vamos a necesitar un servicio. Si por ejemplo, creamos un Sub proceso para bajarnos un fichero largo y salimos de la aplicación, el código quedará detenido y el fichero no se bajará hasta que volvamos a entrar en la aplicación. Creo que con esto queda claro de la necesidad de emplear un método que cuando salgamos de la aplicación siga corriendo un código que haga lo que necesitemos.
La ventaja de los Servicios, es que “casi” (luego hablaremos de este casi ) no se ven afectados por la actividad visible actual. Esto nos permite ejecutar tareas en segundo plano. Siendo las notificaciones de la barra de estado una forma de interactuar con el usuario, ya que los servicios no tienen ningún otro elemento visible. Tampoco pueden mostrar ningún cuadro de dialogo, con excepción de los mensajes “ToastMessageShow” . Tener en cuenta que cuando se produzca un error no veras el típico mensaje de “Desea continuar?” , si no “El proceso X se ha detenido”
Para los que empezáis en B4A deciros que el uso de los servicios es mas sencillo de lo que en principio puede parecer, y dependiendo de que tarea a hacer mucho mas fácil que hacerlo en un Activity, por ejemplo, un servicio no se vuelve a crear cuando el usuario gira la pantalla , un servicio está siempre en marcha. Por otro lado el código escrito en el servicio no es un código especial.
Aunque lo he explicado en otros Post, vuelvo a recordarlo por que es importante saber como Android elige que proceso matar cuando se tiene poca memoria. Y esto dependerá de uno de los tres estados que puede estar el proceso:
Primer Plano (Foreground): El usuario ve al menos un Activity del proceso.
Segundo Plano (Background): Ninguna de las actividades del proceso son visibles, sin embargo hay un Servicio iniciado.
En Pausa (Paused): No hay actividades visibles y servicios no iniciados.
Los Procesos en Pausa son los primeros que mata Android cuando sea necesario.
Si sigue sin haber suficiente memoria, se matarán los procesos de fondo.
Por lo general no se suele matar procesos en primer plano.
Para añadir un Servicio a nuestro proyecto, solo tienes que ir a Project/ Add New Module / Service Module
La plantilla para nuevos servicios es la siguiente:
Sub Process_Globals: Aquí se declaran las variables globales del servicio. No hay otro Sub Globals como en el Activity, ya que un servicio no admite objetos Activity. No emplees aquí SUB procesos, solo declara variables, si empleas otro código puede fallar.
Las variables declaradas aquí se mantienen y son accesibles desde otros módulos.
Sub Service_Create: Entra la primera vez que se inicia el servicio. Aquí puedes inicializar los procesos y las variables globales. Al iniciar un Servicio, este se mantiene en marcha hasta que se llame a StopService o hasta que se destruya
Sub Service_Start: Entra cada vez que llames a StartService o StartServiceAt. Cuando estos Sub ejecuta el proceso pasa a primer plano. Lo que significa que el Sistema Operativo no va a matar el proceso hasta que este Sub termine de ejecutarse. Si quieres ejecutar algún código cada cierto tiempo, es en este Sub donde debes poner el código, por ejemplo:
Sub Service_Destroy: Entra cuando se llama a StopService. El servicio no se ejecuta después de este Sub hasta que lo vuelva a llamar StartService, que volverá a llamar a Sub Service_Create y después Sub Service_Start.
Para llamar y matar a un Servicio:
Varios apuntes:
Los temporizadores en un servicio es malo ponerlos (por no decir que prohibitivos), y lo mas seguro es que al final fallen, normalmente Android puede matar ese servicio y al cabo del tiempo lo volverá a poner en marcha, por lo tanto los temporizadores no funcionaran.
Puedes emplear el siguiente código para que Android no mate el Servicio. Android en un momento dado puede que pare el servicio y lo vuelva a iniciar al cabo de unos segundos, ese código se pone para eso, para que en caso de que Android o el usuario al borrar los procesos de fondo o la memoria si el servicio se para, vuelva a ponerse en marcha.
Para que el Servicio se ejecute cuando reinicies el dispositivo emplea este código
Crea una variable global, y cuando quieras matar el Servicio, en Sub Service_Destroy la consultas y actúa en consecuencia. Por ejemplo, si tu no has matado el servicio puedes consultar esta variable y volver a ejecutar el Servicio (te aconsejo que la guardes en un fichero) .
También te puede servir si has puesto la condición que el Servicio se ejecute al Reiniciar el dispositivo (#StartAtBoot: true), consultándola en el Sub Service_Start (StartingIntent As Intent) , y si tienes el servicio parado por la variable, puedes forzar a pararlo.
Si quieres saber el estado del Servicio (parado o en marcha), puedes emplear IsPaused(NombreDelServicio)
Sobre el “casi” del principio del tutorial :
Hace bastante tiempo me encontré el problema que Android me mataba el servicio al limpiar la memoria, lo expuse en el foro general (inglés), por que para mi el problema no era que matase Android un servicio al limpiar la memoria, el problema era que volvía a entrar pero en el Sub Service_Create, con todas las variables a cero o vacías las cadenas, y lo peor era que no entraba en Sub Service_Destroy , por que si al limpiar memoria hubiese entrado aquí, era fácil saber cuando te lo mataban.
Después de mucho discutir en el foro, la respuesta fue esta:
The correct statement is that there is no guarantee that Service_Destroy will be raised when the process is killed. The same is correct for Activity_Pause.
La traducción de esto de mi inglés seria:
La afirmación correcta es que no hay ninguna garantía de que Service_Destroy se activará cuando se mata el proceso. Lo mismo es correcto para Activity_Pause.
La conclusión es clara, Android te puede matar un servicio y no activar el Sub Service_Destroy .
Por eso es interesante poner lo que he comentado mas arriba (#StartCommandReturnValue: android.app.Service.START_STICKY) , de esta forma lo volverá a llamar después de matarlo y aunque el proceso volverá a empezar desde cero, al menos seguirá en marcha.
También es interesante no hacer llamadas al Servicio con tiempos muy largos.
Si tu aplicación te lo permite, vuelve a llamar al Servicio con un StartServiceAt .
Y si no te importa tener una notificación en la barra de estado, puedes utilizar Service.StartForeground , incluso si el icono que conlleva emplear esto te molesta, puedes emplear una imagen invisible.
Espero no haberme olvidado nada, si es así me lo comentáis y ampliamos el tutorial
Saludos
Después de ver que hay bastante foreros interesados en el tema, e incluso entolium me pidio hacer un tutorial sobre esto, abro este tutorial para intentar aclarar los conceptos mas generales de los Módulos de Servicios.
Si eres nuevo en B4A te recomiendo que antes leas la “Guia rápida del Ciclo de Vida de Android.” (http://www.b4x.com/android/forum/threads/b4a-tutorial-guia-rápida-del-ciclo-de-vida-de-android.38760/#post-229579) , así se entenderá mucho mejor como funciona y actúa un Servicio.
Cuando una aplicación no está visible su código está en pausa (Activity_Pause) . Por lo tanto utilizando solo el código de los Activity no será posible ejecutar ningún código. Esto es la base para entender por que vamos a necesitar un servicio. Si por ejemplo, creamos un Sub proceso para bajarnos un fichero largo y salimos de la aplicación, el código quedará detenido y el fichero no se bajará hasta que volvamos a entrar en la aplicación. Creo que con esto queda claro de la necesidad de emplear un método que cuando salgamos de la aplicación siga corriendo un código que haga lo que necesitemos.
La ventaja de los Servicios, es que “casi” (luego hablaremos de este casi ) no se ven afectados por la actividad visible actual. Esto nos permite ejecutar tareas en segundo plano. Siendo las notificaciones de la barra de estado una forma de interactuar con el usuario, ya que los servicios no tienen ningún otro elemento visible. Tampoco pueden mostrar ningún cuadro de dialogo, con excepción de los mensajes “ToastMessageShow” . Tener en cuenta que cuando se produzca un error no veras el típico mensaje de “Desea continuar?” , si no “El proceso X se ha detenido”
Para los que empezáis en B4A deciros que el uso de los servicios es mas sencillo de lo que en principio puede parecer, y dependiendo de que tarea a hacer mucho mas fácil que hacerlo en un Activity, por ejemplo, un servicio no se vuelve a crear cuando el usuario gira la pantalla , un servicio está siempre en marcha. Por otro lado el código escrito en el servicio no es un código especial.
Aunque lo he explicado en otros Post, vuelvo a recordarlo por que es importante saber como Android elige que proceso matar cuando se tiene poca memoria. Y esto dependerá de uno de los tres estados que puede estar el proceso:
Primer Plano (Foreground): El usuario ve al menos un Activity del proceso.
Segundo Plano (Background): Ninguna de las actividades del proceso son visibles, sin embargo hay un Servicio iniciado.
En Pausa (Paused): No hay actividades visibles y servicios no iniciados.
Los Procesos en Pausa son los primeros que mata Android cuando sea necesario.
Si sigue sin haber suficiente memoria, se matarán los procesos de fondo.
Por lo general no se suele matar procesos en primer plano.
Para añadir un Servicio a nuestro proyecto, solo tienes que ir a Project/ Add New Module / Service Module
La plantilla para nuevos servicios es la siguiente:
B4X:
Sub Process_Globals
End Sub
Sub Service_Create
End Sub
Sub Service_Start (StartingIntent AsIntent)
End Sub
Sub Service_Destroy
End Sub
Sub Process_Globals: Aquí se declaran las variables globales del servicio. No hay otro Sub Globals como en el Activity, ya que un servicio no admite objetos Activity. No emplees aquí SUB procesos, solo declara variables, si empleas otro código puede fallar.
Las variables declaradas aquí se mantienen y son accesibles desde otros módulos.
Sub Service_Create: Entra la primera vez que se inicia el servicio. Aquí puedes inicializar los procesos y las variables globales. Al iniciar un Servicio, este se mantiene en marcha hasta que se llame a StopService o hasta que se destruya
Sub Service_Start: Entra cada vez que llames a StartService o StartServiceAt. Cuando estos Sub ejecuta el proceso pasa a primer plano. Lo que significa que el Sistema Operativo no va a matar el proceso hasta que este Sub termine de ejecutarse. Si quieres ejecutar algún código cada cierto tiempo, es en este Sub donde debes poner el código, por ejemplo:
B4X:
' vuelvo a llamar al servicio en el tiempo fijado
StartServiceAt("", DateTime.Now + Minutos * DateTime.TicksPerMinute, True) ‘ para minutos
StartServiceAt("", DateTime.Now + Segundos * DateTime. TicksPerSecond, True) ‘ para segundos
Sub Service_Destroy: Entra cuando se llama a StopService. El servicio no se ejecuta después de este Sub hasta que lo vuelva a llamar StartService, que volverá a llamar a Sub Service_Create y después Sub Service_Start.
Para llamar y matar a un Servicio:
B4X:
StartService(NombreDelServicio) ‘ Llamar al Servicio
StopService(NombreDelServicio) ‘ matar el Servicio
CancelScheduledService(NombreDelServicio) ' Para si has realizado un StartServiceAt
Varios apuntes:
Los temporizadores en un servicio es malo ponerlos (por no decir que prohibitivos), y lo mas seguro es que al final fallen, normalmente Android puede matar ese servicio y al cabo del tiempo lo volverá a poner en marcha, por lo tanto los temporizadores no funcionaran.
Puedes emplear el siguiente código para que Android no mate el Servicio. Android en un momento dado puede que pare el servicio y lo vuelva a iniciar al cabo de unos segundos, ese código se pone para eso, para que en caso de que Android o el usuario al borrar los procesos de fondo o la memoria si el servicio se para, vuelva a ponerse en marcha.
B4X:
#Region Service Attributes
#StartCommandReturnValue: android.app.Service.START_STICKY
Para que el Servicio se ejecute cuando reinicies el dispositivo emplea este código
B4X:
#Region Service Attributes
#StartAtBoot: true
Crea una variable global, y cuando quieras matar el Servicio, en Sub Service_Destroy la consultas y actúa en consecuencia. Por ejemplo, si tu no has matado el servicio puedes consultar esta variable y volver a ejecutar el Servicio (te aconsejo que la guardes en un fichero) .
También te puede servir si has puesto la condición que el Servicio se ejecute al Reiniciar el dispositivo (#StartAtBoot: true), consultándola en el Sub Service_Start (StartingIntent As Intent) , y si tienes el servicio parado por la variable, puedes forzar a pararlo.
Si quieres saber el estado del Servicio (parado o en marcha), puedes emplear IsPaused(NombreDelServicio)
Sobre el “casi” del principio del tutorial :
La ventaja de los Servicios, es que “casi” no se ven afectados por la actividad visible actual.
Hace bastante tiempo me encontré el problema que Android me mataba el servicio al limpiar la memoria, lo expuse en el foro general (inglés), por que para mi el problema no era que matase Android un servicio al limpiar la memoria, el problema era que volvía a entrar pero en el Sub Service_Create, con todas las variables a cero o vacías las cadenas, y lo peor era que no entraba en Sub Service_Destroy , por que si al limpiar memoria hubiese entrado aquí, era fácil saber cuando te lo mataban.
Después de mucho discutir en el foro, la respuesta fue esta:
The correct statement is that there is no guarantee that Service_Destroy will be raised when the process is killed. The same is correct for Activity_Pause.
La traducción de esto de mi inglés seria:
La afirmación correcta es que no hay ninguna garantía de que Service_Destroy se activará cuando se mata el proceso. Lo mismo es correcto para Activity_Pause.
La conclusión es clara, Android te puede matar un servicio y no activar el Sub Service_Destroy .
Por eso es interesante poner lo que he comentado mas arriba (#StartCommandReturnValue: android.app.Service.START_STICKY) , de esta forma lo volverá a llamar después de matarlo y aunque el proceso volverá a empezar desde cero, al menos seguirá en marcha.
También es interesante no hacer llamadas al Servicio con tiempos muy largos.
Si tu aplicación te lo permite, vuelve a llamar al Servicio con un StartServiceAt .
Y si no te importa tener una notificación en la barra de estado, puedes utilizar Service.StartForeground , incluso si el icono que conlleva emplear esto te molesta, puedes emplear una imagen invisible.
Espero no haberme olvidado nada, si es así me lo comentáis y ampliamos el tutorial
Saludos
Last edited: