Android Tutorial Starter Service - Consistent & Single Entry Point

Status
Not open for further replies.
One of the challenges that developers of any non-small Android app need to deal with, is the multiple possible entry points.

During development in almost all cases the application will start from the Main activity.
Many programs start with code similar to:
B4X:
Sub Activity_Create (FirstTime As Boolean)
If FirstTime Then
  SQL.Initialize(...)
  SomeBitmap = LoadBitmap(...)
  'additional code that loads application-wide resources
End If
End Sub

Everything seems to work fine during development. However the app "strangely" crashes from time to time on the end user device.
The reason for those crashes is that the OS can start the process from a different activity or service. For example if you use StartServiceAt and the OS kills the process while it is in the background.
Now the SQL object and the other resources will not be initialized.

Starting from B4A v5.20 there is a new feature named Starter service that provides a single and consistent entry point. If the Starter service exists then the process will always start from this service.

The Starter service will be created and started and only then the activity or service that were supposed to be started will start.
This means that the Starter service is the best place to initialize all the application-wide resources.
Other modules can safely access these resources.
The Starter service should be the default location for all the public process global variables. SQL objects, data read from files and bitmaps used by multiple activities should all be initialized in the Service_Create sub of the Starter service.

Notes

  • The Starter service is identified by its name. You can add a new service named Starter to an existing project and it will be the program entry point.
  • This is an optional feature. You can remove the Starter service.
  • You can call StopService(Me) in Service_Start if you don't want the service to keep on running. However this means that the service will not be able to handle events (for example you will not be able to use the asynchronous SQL methods).
  • The starter service should be excluded from compiled libraries. Its #ExcludeFromLibrary attribute is set to True by default.
  • The starter service should never be explicitly started. This means that if you want to start a service at boot then add a different service.
Android 8+ considerations
  • Starting from Android 8, the OS kills services while the app is in the foreground.
  • If you are using B4A v8.30+ then the starter service will not be killed until the whole process is killed.
  • You will see a message such as this one, 60 seconds after the app has moved to the background:
    ** Service (starter) Destroy (ignored)**
  • The above message means that the actual service is destroyed but the starter service is still available.
  • Don't make the starter service a foreground service. It will not work once the backed service is destroyed.
  • If you want to do a background task then you should add another service.
  • Related tutorial: Automatic Foreground Mode
 
Last edited:

bgsoft

Well-Known Member
Licensed User
Longtime User
Thank you very much Erel
 

luke2012

Well-Known Member
Licensed User
Longtime User
Hi @Erel. Which is the correct way to progammatically restart the Starter from another module (ex. Main module) when the App is already running?

Thanks in advance for your reply!
 

luke2012

Well-Known Member
Licensed User
Longtime User
Why do you need to restart it? Don't stop it and it will always run (while your process is alive).

@Erel you are right :) I'm looking for a better solution to my problem.
The problem is that I Init a net socket connection (net lib ---> https://goo.gl/JFbTAW) within the starter service.

All the connection parameters (IP and port) are passed reading from the realted preference manager fields (this to let user customize the connection).
So if the user change this parameters I need to reconnect the socket connection using this new putted paremeters (theorically the simple way is to restart the starter).

But probably isn't the most correct and optimized way.

Probably the correct solution is to call the "Connect" method form the module (ex. Main) in order to make a new connection using the new parameters:

B4X:
Starter.so.Close
Starter.so.Connect (host, port, 0)

Am I right?
 

Rusty

Well-Known Member
Licensed User
Longtime User
The main problem I see, as Dave O observed in post #27, is that ALL public variables declared in starter must be prefixed with the word "Starter." even within MAIN.

B4X:
' STARTER SERVICE
Sub Process_Globals
    Dim TestVar As String
End Sub
B4X:
'MAIN ACTIVITY (or anywhere the Starter variables are used)
Sub Activity_Create(FirstTime As Boolean)
    Starter.testvar
End Sub

This means that where MAIN has always been assumed (within MAIN) there is no "qualification" and the variable, if declared in MAIN, would be accessed as

B4X:
testvar = "x"

'if declared within starter, this code would be (within MAIN and all modules and services)
Starter.testvar = "x"

It's too bad that the public variables within Starter couldn't be used as the ones declared within MAIN. (I know that within other activities and services the variable would be used by MAIN.testvar = "x", but this has been inherent in the development from the beginning)

To me, this renders this less useful, but maybe I'm lazy too:)
Rusty
 

RVP

Active Member
Licensed User
Longtime User
So I rewrote an app to use the starter service, however now if I swipe the app away, and then restart it, the Main Activity is not displayed,

The log shows this:

B4X:
** Activity (main) Pause, UserClosed = false **
** Activity (main) Create, isFirst = false **
** Activity (main) Resume **

How do I get my Main Activity to redisplay when I restart the App?

Never mind. I had the Loadlayout being done inside a if FirstTime then .. End If, but turns out that isn't required. I have been doing this since day one, as I am sure I saw it shown that way in an example at some point and thought it was required to only load it once.

Guess not.
 
Last edited:

DonManfred

Expert
Licensed User
Longtime User

RjCl

Member
Licensed User
Longtime User
No one has yet said how to make the starter service ?

Is it Project -> Add New Module -> Service Module, and name it Starter ?

If not how ?
 
Last edited:

DonManfred

Expert
Licensed User
Longtime User
No one has yet said how to make the starter service ?

Read post #1 of this thread

Notes

  • The Starter service is identified by its name. You can add a new service named Starter to an existing project and it will be the program entry point.
  • This is an optional feature. You can remove the Starter service.
  • You can call StopService(Me) in Service_Start if you don't want the service to keep on running. However this means that the service will not be able to handle events (for example you will not be able to use the asynchronous SQL methods).
  • The starter service should be excluded from compiled libraries. Its #ExcludeFromLibrary attribute is set to True by default.
  • The starter service should never be explicitly started. This means that if you want to start a service at boot then add a different service.
 

Dave O

Well-Known Member
Licensed User
Longtime User
No, but I get around this by putting all global variables into a code module called "c" (for "common"). At least that cuts down some of the clutter to "c.xxxxx".
 

LucaMs

Expert
Licensed User
Longtime User
If the os kills your app while it is in the background then it can resume from the last activity.
This would really be a huge idiocy of Android (it is how Android "works", right, not a b4A "bug")!
Are you sure?
If so, I don't see other way to handle this case that ALLWAYS start Main directly from Service_Start... or not? And if I do so, might Main run twice (will two calls added to a queue?)
 
Last edited:

Erel

B4X founder
Staff member
Licensed User
Longtime User
Are you sure?
Yes.

I don't think that it is a huge idiocy of Android. It is a very logical behavior. If the user switches to a different app, maybe to copy some text related to your app, and quickly returns to your app then the user expects to return to the same point. It is possible that the OS will kill the process while the app is in the background.

You can kill all activities when they are paused and the app will start from the main one.
 

LucaMs

Expert
Licensed User
Longtime User
If the user switches to a different app, maybe to copy some text related to your app, and quickly returns to your app then the user expects to return to the same point.
This is logical. It is what you do on Windows, using more than one sw at the same time. But if you "kill" (close) one of this sw and open again it, it will not start "randomly", but from the entry point, from the beginning (yes, I know, you created the Starter Service for this reason but I think this is a workaround of a big defect of Android.


You can kill all activities when they are paused and the app will start from the main one.
This solution, used only for rare cases where the app is killed by the operating system, may have contraindications (Activities will start from Activity_Create and not from Resume).

I don't think that it is a huge idiocy of Android.
Me too; I think it is more than huge :):mad:
 
Last edited:

LucaMs

Expert
Licensed User
Longtime User
You can kill all activities when they are paused and the app will start from the main one.
If so, I don't see other way to handle this case that ALLWAYS start Main directly from Service_Start... or not? And if I do so, might Main run twice (will two calls added to a queue?)
Could this last "mehod" create problems (related to queued calls, adding StartActivity(Main) as last statement in Service_Start)?
 

LucaMs

Expert
Licensed User
Longtime User
Another doubt. If this is Android's behavior, how did you make Starter always run first in any situation, even after the system killed the app when it was in background?

The authors of the Android could do the same thing, so that Main is always started first.

Or maybe you can do it.
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
If this is Android's behavior, how did you make Starter always run first in any situation, even after the system killed the app when it was in background?
The starter service feature is quite complicated. Under the hood the target activity or service start the starter service and make sure that Service_Create of the starter service is called before the target module code starts.

The authors of the Android could do the same thing, so that Main is always started first.
Or maybe you can do it.
B4A will not change the default Android behavior. If you want to do it in your app then there are all kinds of ways to do it. For further discussion you should start a new thread.
 
Status
Not open for further replies.
Top