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:

incendio

Well-Known Member
Licensed User
Longtime User
Yeah, I mean delayed.

The solution still need service in main app to regularly check finished tasks on service app.
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
The code you write in the service is executed in the main thread.

If you did encounter any performance issues then you should post more specific details about your implementation. SQL library does support asynchronous operations. So if it is slowing down your app (and you are correctly using transactions) then you should use these methods.
 

incendio

Well-Known Member
Licensed User
Longtime User
I have use sqlite in memory and transaction,but not asynchronous operations.
During an insert operation (about 20K records) and some calc, there was a delayed in UI about 3-4 secs. Will try asynchronous operations and see what happens.
 

incendio

Well-Known Member
Licensed User
Longtime User
Single transaction? What do you mean?

I wrote my code like this
Begin transaction
do some insert
do some calc/other proc
end transaction
 

incendio

Well-Known Member
Licensed User
Longtime User
Yes I did the loops for .. next inside the insert operation.

3-4 seconds is depend, when it is idle or users waiting for some more long operation is ok, but when in the middle of doing some input, is a bit disturbing, especially when you need to re-download data again.

Wouldn't mind at all if in the future update, B4A has a feature to running service in its own thread :)
 

Troberg

Well-Known Member
Licensed User
Longtime User
I've made a media player with a service as the player back end. Works nicely as long as the front end is visible, but if another activity is visible or the screen is off, it stops playing after a track is complete. The Complete event for the media player fires just fine, and in it, I call the activity using this code:

B4X:
CallSubDelayed(Main,"MP_Complete")

In my mind, it should start the Main activity if needed, then call MP_Complete. However nothing happens.

The log says this:

sending message to waiting queue (CallSubDelayed - MP_Complete)
running waiting messages (1)

Then, nothing happens.

If I manually bring the activity to the front again, the code continues as expected.

Am I doing something wrong here? What is the preferred method to call an activity from a service?
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
See the tutorial: Using CallSubDelayed to interact between activities and services

CallSubDelayed will not start the activity if the app is in the background. Why do you need to start the activity at all? It is not recommended to start an activity from a service when the app is in the background as the user is doing something else.
 

Troberg

Well-Known Member
Licensed User
Longtime User
Well, it's kind of a "supposed to always be at the front"-app.

Anyway, I'll just have to rethink my code a bit, and move more stuff to the service.
 

Antony Danby

Member
Licensed User
Longtime User
Erel

Its not clear in any of the documentation anywhere, but appears as if I cannot declare and use a class instance within a service is this true ?
My error at compilation was:

Error compiling program
Error description: Cannot access activity object from sub
Process_Globals
Occurred on line: 12
Dim control As MainControl
Word: maincontrol

If this is true it wrecks my whole design.

I wanted to do: Dim control As MainControl within the Service's Process_Globals ( where MainControl was my controller which was itself looking the results from other instantiated classes ) and then do control.Initialize(Me,"States_Changed") and have the result from listening to the other classes come back into a single event ( States_Changed ). It's a simple controller design and should work.

However, if this is not true I don't even know how to do what I need to do and what really would be the point of a service if you can't invoke this kind of design.?
 

Antony Danby

Member
Licensed User
Longtime User
No.

However if your class has a global variable which requires an activity context (like a View) then you cannot use it from a service.

Ok

This is what I thought; my MainControl is a class and is a "black box" and contains no variables or calls to any View.
Oh; hang on. I have one in my lower levels; ah bad design ;-)

Thanks for your quick response and good reply Erel.

I knew my design wasn't wrong ( Delphi coder for a long time ) and i did think B4A would be capable of it.
Just a B4A newbie mistake !
 

Antony Danby

Member
Licensed User
Longtime User
Maybe I'm wrong, but I think that the classes should not contain global variables. It is better pass the value using the properties.

No disagreement from me on that point.
I try to use private variables and only expose to the interface those variables that I need for other classes.
The whole secret behind designing good classes hangs on what you expose to the outside world and what you don't
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
Maybe I'm wrong, but I think that the classes should not contain global variables.
I think that you are confusing public variables with global variables. While personally I use both some consider public variables to be problematic.

Global variables (instance variables) however are very important as they store the instance state.
 
Top