Spanish [B4A] [Tutorial] Módulos de Servicio

bgsoft

Well-Known Member
Licensed User
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.basic4ppc.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 :D) 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:

dantek

New Member
Muchas gracias, soy nuevo en este foro y trato de aprender de los que ya estan trabajando con esta herramienta.. ni bien pueda desarrollar algo "interesante" lo comparto con Uds, ya que con el aporte de todos, se amprende..
saludos
 

Glitchs

Member
Licensed User
Muy buena explicación. Que pena que no lo hubiera visto antes y no perder el tiempo releyendo en inglés este tema que aunque sencillo no lo es tanto cuando tienen que interactuar servicios y actividades. De hecho he tenido que utilizar un TTS y un GPS para cada uno porque si utilizaba uno para los dos la app iba bastante mal. Lo único que he podido compartir entre activitys y servicios son variables booleanas y numericas. Estoy haciendo una aplicación y lo que estoy utilizando para que no me mate el servicio es Service.StartForeground, y por el momento nunca me ha asesinado a mis queridos servicios.
Salu2
 

Raymundo Gomora

Member
Licensed User
Esta mas que completa tu explicación, solo una pregunta al dar clic en la notificación esta no desaparece de la barra de estado, ya le puse notificacion.autocancel y si la desaparece pero al parecer ya no me aparecen las demás notificaciones que mando, quisiera que siguiera mandando nuevas notificaciones mas no encuentro el problema, no se si me puedan ayudar.
 

bgsoft

Well-Known Member
Licensed User
Hola Raymundo

Quizas este no sea el mejor hilo para contestar a tu respuesta, por que casi no tiene que ver nada la notificacion con un servicio, pero bueno, haré una excepcion, por que siempre lo que hago es pedir que se abra un nuevo post, ya que asi todos podemos beneficiarnos de esa respuesta que aqui quedara escondida. Si no se te solociona el problema, por favor abre un nuevo hilo y seguro que entre todos podemos resolver tu problema.


Supongo que tu habras activado la notificación asi:

B4X:
  Notificacion.Notify(1)  ' el uno es el id de esa notificación, puedes poner una variable
' Para cancelarla, simplemente la cancelas con el mismo id que la activaste, en nuestro ejemplo un 1
B4X:
  Notificacion.Cancel(1)
' --------------------------------- 


'  Te aconsejo que la pongas aqui:
Sub Activity_Resume
Notificacion.Cancel(1)

' de esa forma, al hacer click en la barra de estado y esta llamar a la aplicacion, cancelaras la notificación

Saludos
 

Raymundo Gomora

Member
Licensed User
Muchas gracias por tu respuesta no se si exista algun link para saber por que cuando ´salgo de la aplicación con ExitApplication deja de funcionar el servicio, ya que si esta la aplicacion abierta el servicio funciona bien pero una vez que salgo de esta dejan de llegarme las notificaciones del servicio.
 

bgsoft

Well-Known Member
Licensed User
Hola

cuando salgo de la aplicación con ExitApplication deja de funcionar el servicio
Respuesta que te aclare tu duda no tengo, por que creo que nadie la tiene, Erel recomienda no llamar a un servico y luego salir con ExitApplication. Yo creo que es por que depende del momento que le pille el lanzamiento del servicio no le da tiempo a entrar y es por eso por lo que no se pone en marcha, sin enbargo si sales normal, al quedar la aplicación en segundo plano si que entra.
Una opcion que tienes es que un timer mire si el servicio está en marcha, y cuando lo esté salir con ExitApplication si tienes la necesidad de salir así.

Saludos
 

Raymundo Gomora

Member
Licensed User
Hola



Respuesta que te aclare tu duda no tengo, por que creo que nadie la tiene, Erel recomienda no llamar a un servico y luego salir con ExitApplication. Yo creo que es por que depende del momento que le pille el lanzamiento del servicio no le da tiempo a entrar y es por eso por lo que no se pone en marcha, sin enbargo si sales normal, al quedar la aplicación en segundo plano si que entra.
Una opcion que tienes es que un timer mire si el servicio está en marcha, y cuando lo esté salir con ExitApplication si tienes la necesidad de salir así.

Saludos
Gracias de antemano, el problema en si no era la instrucción de ExitApplication, el detalle estaba en la consulta sql ya que la variable que utilizava para la validacion quedaba en blanco y por eso no me hacia la consulta correctamente, al parecer ya quedo solucionado, agradezco de nuevo tu apoyo. Saludos.
 
excelente información,quisiera hacer una consulta, tengo mi activity y un service, si cierro la activity como hago para que el service que esta trabajando de fondo al recibir un evento la ejecute y la coloque en primer plano nuevamente, agradezco su colaboracion.
 

bgsoft

Well-Known Member
Licensed User
Hola

Es muy facil, llámalo como si llamaras a otro formulario:

B4X:
StartActivity("main") ' o el nombre del formulario que quieras abrir

' tambien si te interesa llamar a un Sub en concreto puedes hacerlo asi:
CallSubDelayed(nombredelformulario,"NombredelSub")

' por si necesitas pasarle un argumento
CallSubDelayed2(nombredelformulario,"NombredelSub", argumento)

Saludos
 

vbmundo

Well-Known Member
Licensed User
Hola Jesus, como estas ?

Queria preguntarte si las variables que estan declaradas en un Servicio pueden consultarse desde un Activity... basicamente porque imagino que lanzas un servicio desde el Activity y luego quieres interactuar con el para desde el FrontEnd mostar determinadas cosas..

Yo imagino usar un Service para ejecutar una sentencia SQL que puede demorar en devolver un resultset ( o como se llame en Java ) y luego usar esos datos desde el Activity para mostrar los resultados...

Saludos
 

rscheel

Well-Known Member
Licensed User
@vbmundo
El servicio tendrías que sincronizar los datos a un bd sqlite en el teléfono de esa manera puedes utilizar en cualquier activity los datos, el servicio igual puede correr cada cierto tiempo aunque la aplicación este cerrada.
 
Top