Android Tutorial Service Modules

This is an old tutorial. Most of the information here is no longer correct.



Basic4android v1.2 adds support for Service modules.
Service modules play an important role in the application and process life cycle.
Start with this tutorial if you haven't read it before: Android Process and activities life cycle
Code written in an activity module is paused once the activity is not visible.
So by only using activities it is not possible to run any code while your application is not visible.
Services life cycle is (almost) not affected by the current visible activity. This allows you to run tasks in the background.
Services usually use the status bar notifications to interact with the user. Services do not have any other visible elements. Services also cannot show any dialog (except of toast messages).
Note that when an error occurs in a service code you will not see the "Do you want to continue?" dialog. Android's regular "Process has crashed" message will appear instead.

Before delving into the details I would like to say that using services is simpler than it may first sound. In fact for many tasks it is easier to work with a service instead of an activity as a service is not paused and resumed all the time and services are not recreated when the user rotates the screen. There is nothing special with code written in service.
Code in a service module runs in the same process and the same thread as all other code.

It is important to understand how Android chooses which process to kill when it is low on memory (a new process will later be created as needed).
A process can be in one of the three following states:
- Foreground - The user currently sees one of the process activities.
- Background - None of the activities of the process are visible, however there is a started service.
- Paused - There are no visible activities and no started services.

Paused processes are the first to be killed when needed. If there is still not enough memory, background processes will be killed.
Foreground processes will usually not be killed.

As you will soon see a service can also bring a process to the foreground.

Adding a service module is done by choosing Project - Add New Module - Service Module.
The template for new services is:
B4X:
Sub Process_Globals

End Sub

Sub Service_Create

End Sub

Sub Service_Start (StartingIntent As Intent)

End Sub

Sub Service_Destroy

End Sub
Sub Process_Globals is the place to declare the service global variables. There is no other Globals sub like in Activity as Service doesn't support Activity objects.
Sub process globals should only be used to declare variables. It should not run any other code as it might fail. This is true for other modules as well.
Note that Process_Global variables are kept as long as the process runs and are accessible from other modules.

Sub Service_Create is called when the service is first started. This is the place to initialize and set the process global variables. Once a service is started it stays alive until you call StopService or until the whole process is destroyed.
Sub Service_Start is called each time you call StartService (or StartServiceAt). When this subs runs the process is moved to the foreground state. Which means that the OS will not kill your process until this sub finishes running. If you want to run some code every couple of minutes / hours you should schedule the next task with StartServiceAt inside this sub.

Sub Service_Destroy is called when you call StopService. The service will not be running after this sub until you call StartService again (which will run Sub Service_Create followed by Sub Service_Start).
Service use cases

As I see it there are four main use cases for services.
- Separating UI code with "business" or logic code. Writing the non-UI code in a service is easier than implementing it inside an Activity module as the service is not paused and resumed and it is usually will not be recreated (like an Activity).
You can call StartService during Activity_Create and from now on work with the service module.
A good design is to make the activity fetch the required data from the service in Sub Activity_Resume. The activity can fetch data stored in a process global variable or it can call a service Sub with CallSub method.

- Running a long operation. For example downloading a large file from the internet. In this case you can call Service.StartForeground (from the service module). This will move your activity to the foreground state and will make sure that the OS doesn't kill it. Make sure to eventually call Service.StopForeground.

- Scheduling a repeating task. By calling StartServiceAt you can schedule your service to run at a specific time. You can call StartServiceAt in Sub Service_Start to schedule the next time and create a repeating task (for example a task that checks for updates every couple of minutes).

- Run a service after boot. By setting the #StartAtBoot attribute to True, our service will run after boot is completed.
Notifications

Status bar notifications can be displayed by activities and services.
Usually services use notifications to interact with the user. The notification displays an icon in the status bar. When the user pulls the status bar they see the notification message.

Example of a notification (using the default icon):

notification_1.png



notification_2.png


The user can press on the message, which will open an activity as configured by the Notification object.

The notification icon is an image file which you should manually put in the following folder: <project folder>\Object\res\drawable.
Accessing other modules

Process global objects are public and can be accessed from other modules.
Using CallSub method you can also call a sub in a different module.
It is however limited to non-paused modules. This means that one activity can never access a sub of a different activity as there could only be one running activity.
However an activity can access a running service and a service can access a running activity.
Note that if the target component is paused then an empty string returns.
No exception is thrown.
You can use IsPause to check if the target module is paused.

For example if a service has downloaded some new information it can call:
B4X:
CallSub(Main, "RefreshData")
If the Main activity is running it will fetch the data from the service process global variables and will update the display.
It is also possible to pass the new information to the activity sub. However it is better to keep the information as a process global variable. This allows the activity to call RefreshData whenever it want and fetch the information (as the activity might be paused when the new information arrived).

Note that it is not possible to use CallSub to access subs of a Code module.

Examples:
Downloading a file using a service module
Periodically checking Twitter feeds
 
Last edited:

ericvanderhoeven

Member
Licensed User
Longtime User
Hi,
I don't know if that's the intended way to use a service, but I'd like to develop a library made up of a single service which run all the day checking some web data every couple of hours.
Each time the service discovers new data it should be able to notify the OS which in turn (on user intervention) has to launch an appropriate app (the one linking in the above mentioned lib?) which has to take care of that new data.

Problem: if the service is in a lib, Notifications can't refer to Main but only to service itself.

So, am I expected to receive the OS notification in the Main part of the service lib and from there start the wanted activity?

Umberto

Hi Umberto,

the answer = yes

From the service module, at your convenience (at that notification of new data or at a timer) you can just start any activity you'd like by using the command StartActivity(ActivityName)

Easy does it..

Eric
 

udg

Expert
Licensed User
Longtime User
@Erel: sorry, I stand corrected. Just saw my question as a way to specify how to combine use of a service and an app.
@eric: thanks, I'll procede that way.

Umberto
 

shmulik taiber

Member
Licensed User
Longtime User
hello Erel .
I've managed to utilize a service module to get data from arduino via usb (asyncstreams) in the background . I've logged it (in the service module) and it's correct in realtime . however, accessing the data from Main module still evades me .
I wrote -
in the service module :
str = BytesToString(Buffer, 0, Buffer.Length, "UTF8")

in Main
lbl_in.Text=arduino.str
or tried :
Log(str)

got nothing . what am I doing wrong ?
 

luke2012

Well-Known Member
Licensed User
Longtime User
Hi @Erel.
I'm using the StartServiceAt in order to schedule a notification in an specifica range (for example : 1 time in a month).
The problem happen when the user restart the smartphone because the service is set to start at boot and the notification appears before the scheduled time.

My target is to activate the notification only on the defined schedule time not at the system boot.
Any solution about it?
 

Roger C

Active Member
Licensed User
Longtime User
A question about starting the service at a specific time...

The task is to make a daily break in a timereport, when entering a new day, the time should be save for the old and a new event should be started for the next day.

This could be done when the user checks out; check if it started on a previous day and then register yesterday as an event and then create and save a new event for the day he checked out.

But I think (for closing each day correct, there might be need for running a report early in the day and the user might not have ended his shift yet) that I would try to have a service running at midnight and close the day before and start a new event.

What is the best way to set the service to start at midnight every day?

I have this code in Service_Start but are not sure that it will set 24 hours until next start. The date will probably be the same day...
Should I add 1 to the date or should I set "00:00:00"?
Can I always be sure that this Service will be called at exactly 23:59:59 every day without delays?

Are there better ways to run a service at an exact time?

B4X:
Dim lTime As Long
   Dim start, endTime As Long

   start = DateTime.now
  endTime = DateTime.DateTimeParse(DateTime.date(DateTime.Now), "23:59:59")
  lTime = Abs(endTime - start)

   StartServiceAt("Midnight",lTime, True)
 

jlperez

Member
Licensed User
Longtime User
Dear friends,
I need to write a hidden service (no user interface) which needs to execute some task every 5 minutes.

Could anyone give some advice on how can I build something like this without interface? How can I invoke such a service and install it on the smartphone?


Thanks in advance

Best regards
Jose Luis
 

jlperez

Member
Licensed User
Longtime User
Set StartAtBoot to true and use StartServiceAt to schedule the service to run every 5 minutes (search the forum there are many examples).

Your app must run at least one time with an activity for the StartAtBoot to work. You can use a transparent activity.
Thank you Erel
 

jefflynn1974

Member
Licensed User
Longtime User
I have two services with start foreground. One of them is a widget. Can they use the same notification or every service must have an own one?
 

lemonisdead

Well-Known Member
Licensed User
Longtime User
How to get a value from activity module to service module?
Hi,
By referencing using the activity's name. For example: n= Main.Variable
The activity's variable should be declared in Process_Globals
 

entolium

Member
Licensed User
Longtime User
Hi,

I have a service reading usbserial GPS. This service send the mocklocaction to the system, and call a activity sub (if not is paused) for show the data.

The problem is when Android go off, or manually off pressing the power button (a short press). When power on (short press again), the service not update the data (the notify icon remains, then, I think the service is running ¿or not?). I must stop and start the service from the activity for return to normal operation.

Why?

I'm crazy with this problem.
 

entolium

Member
Licensed User
Longtime User
Thank you Erel for your respone. Yes, I call StartForeground.

This is my code in Service Start:

B4X:
Sub Service_Start (StartingIntent As Intent)

    'StartServiceAt("", DateTime.Now + 5 * DateTime.TicksPerSecond, False) 'schedule the next task to run in 5 seconds.
    Service.StartForeground(1, Notificacion1)

    If usb.UsbPresent <> usb.USB_DEVICE Then
       
        Notificacion1.SetInfo("GPSusb","Dispositivo no compatible",Main)
        Notificacion1.Notify(1)
       
    Else
        Try
            If (usb.HasPermission) Then
                Dim dev As Int
                dev = usb.Open(4800)       
                If dev = usb.USB_DEVICE Then
                    astreams.Initialize(usb.GetInputStream, Null, "astreams")
                    Notificacion1.SetInfo("GPSusb","Dispositivo conectado",Main)
                    Notificacion1.Notify(1)
                Else
                    usb.Close
                    StartServiceAt("", DateTime.Now + 5 * DateTime.TicksPerSecond, True) 'schedule the next task to run in 5 seconds.
                    Notificacion1.SetInfo("GPSusb","Dispositivo no compatible",Main)
                    Notificacion1.Notify(1)
                End If
            Else
                usb.RequestPermission
            End If
        Catch
            usb.Close
            StartServiceAt("", DateTime.Now + 5 * DateTime.TicksPerSecond, True) 'schedule the next task to run in 5 seconds.
            UltimoError=LastException.Message
        End Try
       
    End If


End Sub
 
Top