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:

schimanski

Well-Known Member
Licensed User
Longtime User
I'm working with two services in my application and it runs very fine, but I ask me, if it is better to work only with one service in my application. It is possible to start one service with Activity create and let the service do all the things, which have to run in the backround. But when I do this, it is nessesary to create different timers in the service. I don't know, what the better programming is.

Rgds
 

LittleEddie

Member
Licensed User
Longtime User
I'm wanting to write a service that has no Front end,

It is called from an Intent in another App with data and does things to the data and then returns data back to the calling App.

I've only been studing this the last half hour and I think it's doable but I thought I'd ask to save some time.

Ed
 

jndaniels1

Member
Licensed User
Longtime User
another service question

I've read the help program; very good reference. Most things I've delt with were activity processes. Now I am wondering about services and threads.
would it be better to run some GPS, TTS, and Voice Recognition as a thread or a service? Being a service i can throw a notice up.

Just a general question; wanting to know which why to approach my next project..

Much appreciate it!
Cheers
.JND.
 

anaylor01

Well-Known Member
Licensed User
Longtime User
I am trying to do exactly like the example but everytime I click the back or home key the app stops running. What am I doing wrong?
 

Taha

Member
Licensed User
Longtime User
It is strange - I made a notification, it appears and works correct but when I press on the notification - the application doesn't open.

A small correction about your example.
The ServiceTimer timer is not needed and is likely to fail eventually.
You should instead schedule the next task with StartServiceAt.
Android will eventually kill your process. By scheduling the next task with StartServiceAt you make sure that a new process will be created when the scheduled time arrives. This is not the case with a regular timer.
Also note that for most application 1 second is a very very short period as it will drain the device battery. Running a task once an hour or every 30 minutes is usually more appropriate.

Updated code:
B4X:
Sub Process_Globals
  Dim Counter As Int 
  Counter=0    
End Sub

Sub Service_Create

End Sub

Sub Service_Start
  Counter=Counter+1 ' This counting will not work for long. If it is important it should be saved in a file instead of a variable.
 StartServiceAt("", DateTime.Now + 30 * DateTime.TicksPerSecond, True) 'schedule the next task to run in 30 seconds.
 ... do the required work
End Sub

It is not recommend to start an activity from a service. As the activity will pop up while the user is busy with something else.
Instead you should show a notification. When the user presses on the notification, the activity will start.

I will post a full example on Sunday.
 

FHEBERT

Member
Licensed User
Longtime User
Hi,
I've a problem when my phone is standby. The timer stopped.
You've a idea ?
My timer is declared in my process globals of the module
thanks for your help
 

BarrySumpter

Active Member
Licensed User
Longtime User
How hard would it be to add the code to make the GPS.B4A a background service?

I've had a good study of the Twitter app but can't work out which is the twitter script and which is the background service script.

Perhaps a Twitter.b4a WITHOUT the server script.
Then I could compare the two.

Any help would be greatly appreciated.

myGPStxt.b4a
B4X:
Sub Process_Globals
   Dim GPS1 As GPS
End Sub

Sub Globals
   
   Dim lblLon As Label
   Dim lblLat As Label
   Dim lblSpeed As Label
   Dim lblSatellites As Label
   Dim lblDateTimeStamp As Label
   
  Dim Map1 As Map
     
End Sub

Sub Activity_Create(FirstTime As Boolean)

   If FirstTime Then
      GPS1.Initialize("GPS")
  End If
   
'   If File.ExternalWritable = False Then
'      Msgbox("Cannot write on storage card.", "")
''      Return
'  End If

   
   Activity.LoadLayout("1")


End Sub

Sub Activity_Resume
   If GPS1.GPSEnabled = False Then
      ToastMessageShow("Please enable the GPS device.", True)
      StartActivity(GPS1.LocationSettingsIntent) 'Will open the relevant settings screen.
   Else
      GPS1.Start(0, 0) 'Listen to GPS with no filters.
   End If
End Sub

Sub Activity_Pause (UserClosed As Boolean)
   GPS1.Stop
End Sub

Sub GPS_LocationChanged (Location1 As Location)

  Dim dt As String
   Dim now As Long
  
   DateTime.DateFormat = "yyyy-MM-dd HH:mm:ss"
   
   now = DateTime.now
   dt = DateTime.Date(now)

        ' Google Maps API calls are in Seconds not minutes
   lblLat.Text = "Lat = " & Location1.ConvertToSeconds(Location1.Latitude)
   lblLon.Text = "Lon = " & Location1.ConvertToSeconds(Location1.Longitude)
   lblSpeed.Text = "Speed = " & Location1.Speed
   lblDateTimeStamp.text = dt

   Map1.Initialize
        ' Google Maps API calls are in Seconds not minutes
   Map1.Put ("Lat", Location1.ConvertToSeconds(Location1.Latitude))
   Map1.Put ("Lon", Location1.ConvertToSeconds(Location1.Longitude))
   Map1.Put ("Speed", Location1.Speed)
   Map1.Put ("DateTimeStamp", dt)
   File.WriteMap(File.DirRootExternal, "GPS.txt", Map1)

End Sub

Sub GPS_UserEnabled (Enabled As Boolean)
   ToastMessageShow("GPS device enabled = " & Enabled, True)
End Sub

Sub GPS_GpsStatus (Satellites As List)
   lblSatellites.Text = "Satellites:" & CRLF
   For i = 0 To Satellites.Size - 1
      Dim Satellite As GPSSatellite
      Satellite = Satellites.Get(i)
      lblSatellites.Text = lblSatellites.Text & CRLF & Satellite.Prn & _
         " " & Satellite.Snr & " " & Satellite.UsedInFix & " " & Satellite.Azimuth _ 
         & " " & Satellite.Elevation
   Next
End Sub
 
Last edited:

BarrySumpter

Active Member
Licensed User
Longtime User
Hi Erel,
Just wanted to bump this request of updating this simple GPS code to be a service to run in the background.
My research indicates that it should only take about 12 to 15 lines of script.
I've had a coupe of goes but can't tell where to put what.
I feel this would be a fantastic way to show how easy it is to convert a b4a app into a service.
As soon as I can see a working service before and after script I'll be able to convert future b4a apps on my own.
I know you're busy and if I'm asking too much just let me know and I'll understand.

If anyone else can assist I'd certaily appreciate any help.

All my best to all.
 

BarrySumpter

Active Member
Licensed User
Longtime User
I've already spent a couple hours to-ing and fro-ing trying to get it to work. :sign0104: :BangHead:
So confused n agro I deleted all my work to start again.
And ran out of energy.
:sign0013:

What a shame.
A great opportunity to learn something from the master. :sign0188:
Missed. :sign0148:

Now I'm thinking if its so easy to do why hasn't someone else posted a solution. :sign0163:
So I'm feeling a bit better about not being able to do it.

I've already posted the GPS script to start from.
I think you could have used that 2 mins to reply toward the 5 mins it would take to make this a service. :sign0188:
Missed.:sign0148:

I've moved back to LiveCode.
They seem to have sorted out their GPS functionality in the latest version releasaed a few days ago.
I was so looking forward to showing off the GPS service that b4a could deliver.
And LC couldn't.
Anoher missed opportunity.:sign0148:

:sign0163:
:sign0163: at all?
:sign0085:
 
Last edited:

Erel

B4X founder
Staff member
Licensed User
Longtime User

rfresh

Well-Known Member
Licensed User
Longtime User
I was wondering if using FTP in a Service does anything funny to the StartServiceAt() statement?

If I'm not downloading or uploading a file, the line

B4X:
StartServiceAt("",DateTime.Now + 20 * 1000,True)

works fine and the service module cycles every 20 seconds.

However, if I throw in a file download or upload, it stops the service cycle (the download and upload is successful). Is there anything I have to do special in a service module when using FTP?

Thanks...
 

rfresh

Well-Known Member
Licensed User
Longtime User
I need to download the file every so many minutes. It's a very small file and I'm set to download it every 10 mins.

Do I need to stop the service module interval just before downloading and then make the StartServiceAt() call again when it's done downloading?
 
Top