Android Question Receivers vs Services (Which one?)

walterf25

Expert
Licensed User
Longtime User
Hi all, I'm working on a small app that connects to a power usb meter via BLE, I need to log voltage and current values vs Time to a csv file, my question is, since I haven't used Services in a long time, I'm not sure of which one to use, I now see that there are Receivers, as if our lives are not complex enough .

I need the app to be able to log data while the app is in the background if the user places the app in the background, I know this should be fairly simple, but do you guys have any advise on whether to use a Receiver or a Service to accomplish this task?

What do you recommend and why?

Thanks everyone!
Walter
 

drgottjr

Expert
Licensed User
Longtime User
in general, to keep an app running (almost) all the time, you need a foreground service.
android can still kill your app, regardless. so you need something to bring it back
to life. that would be a receiver. if you wanted your app to be launched as soon as the
device is booted, you would need a receiver. if your app polls the meter periodically for
a reading, a foreground service might be enough. if your app is being used to detect voltage
fluctuations at, eg, the nuclear power station at diablo canyon, good luck and godspeed.
 
Upvote 0

walterf25

Expert
Licensed User
Longtime User
Hi @drgottjr thanks for your reply, I have a service now which seems to be working, however I haven't really done extensive testing as far as how long the service stays running, but eventually I would like the service to run for let's say longer than 1 hour, in this case, you mentioned a Receiver, how would the receiver come into play, how would the receiver re-start the service if it gets killed by the OS?

Regards,
Walter
 
Upvote 0

LucaMs

Expert
Licensed User
Longtime User
you mentioned a Receiver, how would the receiver come into play, how would the receiver re-start the service if it gets killed by the OS?
I don't like new things, I'm too old, probably. On the other hand, receivers have existed since the first version of Android (API 1), but in B4A we didn't need them.

We should read Erel's tutorial about it (even me, who only read it very quickly).
As far as I understand and remember, receivers are very short-lived and should only be used to launch "something" initially.

For your purpose, you should search on the site how to run "stuff" in the background. You will find that the most often suggested example is "Background location tracking".
Also see this:
https://www.b4x.com/android/forum/threads/play-music-in-the-background.160497/
 
Upvote 0

drgottjr

Expert
Licensed User
Longtime User
for what it may be worth:
from https://developer.android.com/develop/background-work/background-tasks/broadcasts
"If you declare a broadcast receiver in your manifest, the system launches your app (if the app is not already running) when the broadcast is sent."
this is an exception to the rule. (the rule being you can't start an activity from the background). an alarm broadcast can launch an app when the alarm is triggered.

judging by the service's only being required for an hour, i would think it would continue running if the user simply started another app (as opposed to, eg, swiping to force killing it). i listen to the radio (background) in an android device for around an hour while flipping back and forth between other activities. i suggest rebuilding the app (as is) under b4a 13, running it and coming back after an hour to see what has happened. i would also test by running the app and then launch some other apps to see what has happened to the monitoring service. and go from there. based on what little we know about the app, there may be nothing that currently causes it to fail under android's ever-changing rules. i imagine a user starting the app to set the service in motion and then starting some other app, maybe to check email or to listen to the radio, while the monitoring service runs its course. i would expect that to occur without incident. i don't image the user starting the service and then force killing the app. if that happened, they restart the app and not kill it this time. i mean, really, how much are you supposed to do? if monitoring is supposed to go on all day, then you run the risk of its being killed due to battery preservation issues. more confusing than receiver vs service is the matter of battery conservation.

before looking into added functionality, i would like to hear whether the apps runs as is under android 14 and address that first. if an app has a service that's supposed to run for 1 hour fails to do that, in general, there isn't any particular signal given to that effect. (after the tree falls in the woods, somebody has to come by later to see if anything has happened.) how you deal with that requires knowing more about what's expected of the app. must the service be started at some specific time or all is lost? can it be run at some later time in the event something prevented it from running at the assigned time? perhaps a foreground service is needed, although there is no guarantee that the os does not kill the app. an alarm could trigger a receiver to make sure the service was running (not an alarm app; this is an alarm broadcast receiver). an alarm broadcast might even allow the user to forget about starting the app. if the app runs fine now, then adding a receiver shouldn't be too difficult. if the app doesn't run correctly now, then there's no sense adding a receiver that continues to make it run incorrectly. i use receivers (eg, for sms management or telling me when a usb device has been plugged in), and i've tested with a receiver launching an app that was not running at all. i'd like to know what i don't know before trying to suggest a solution (to a problem that i'm unaware of).
 
Upvote 0

lip

Active Member
Licensed User
Longtime User
I have used Foreground Services to keep things going in the background and recently had to move to Android 14 (API 34). I'm starting the services from the App so don't need any clever way to start them in the background. The solution for me was simply to declare their usage in the manifest, then they seem to work as before. I think this is mainly to help the Playstore review process check that you're not trying to do anything dodgy.

Manifest - change ServiceName to whatever you service is called:
AddPermission(android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE)
SetServiceAttribute(ServiceName, android:foregroundServiceType, "connectedDevice")
 
Upvote 0

walterf25

Expert
Licensed User
Longtime User
Here's what I have so far....

I decided to go with a Service since upon reading on Receivers, I noted that they are short lived or only used to run for a short period of time.
With that said, I did some testing last night, and I was able to run the service for around 2 hours until I had to stop it as I was ready to go to bed, here's what my manifest file looks like.
Manifest code:
AddManifestText(
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="34"/>
<supports-screens android:largeScreens="true"
    android:normalScreens="true"
    android:smallScreens="true"
    android:anyDensity="true"/>)
SetApplicationAttribute(android:icon, "@drawable/icon")
SetApplicationAttribute(android:label, "$LABEL$")
CreateResourceFromFile(Macro, Themes.LightTheme)
'End of default text.

AddPermission(android.permission.ACCESS_FINE_LOCATION)
AddPermission(android.permission.BLUETOOTH_SCAN)
AddPermission(android.permission.BLUETOOTH_CONNECT)

SetApplicationAttribute(android:largeHeap,"true")

AddManifestText(<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="19" />
)
AddPermission(android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE)
SetServiceAttribute(LogData, android:foregroundServiceType, "connectedDevice")

The code in my LogData Service
LogData Service:
#Region  Service Attributes
    #StartAtBoot: False
    
#End Region

Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'These variables can be accessed from all modules.
    Private usbmeter As Bitmap
End Sub

Sub Service_Create
    Service.AutomaticForegroundMode = Service.AUTOMATIC_FOREGROUND_WHEN_NEEDED
    usbmeter.Initialize(File.DirAssets, "fnb58.JPG")
    Log("is service running: " & IsPaused(Me))
    Service.StartForeground(1, Simple_Notification)
End Sub

Sub Service_Start (StartingIntent As Intent)
    
    Service.StopAutomaticForeground 'Call this when the background task completes (if there is one)
End Sub

Sub Service_Destroy
    '''Service.StopAutomaticForeground
    StopService(Me)
    Log("LogData Service destroyed...")
End Sub

Sub Simple_Notification As Notification
    Dim n As NB6
    n.Initialize("default", Application.LabelName, "DEFAULT").AutoCancel(True).SmallIcon(usbmeter)
    Dim notify As Notification
    notify = n.Build("FNB58 USB Meter", "Collecting data...", "tag1", Main)
    notify.notify(4) 'It will be Main (or any other activity) instead of Me if called from a service.
    Return notify
End Sub

Sub SaveData(data As String)
    LogColor("saving data: " & data, Colors.Red)
    Dim tw As TextWriter
    tw.Initialize(File.OpenOutput(B4XPages.MainPage.rp.GetSafeDirDefaultExternal("data"), "csvdata"&"_"&B4XPages.MainPage.date&"_"&B4XPages.MainPage.time&".csv", True))
    tw.WriteLine(data)
    tw.Close
End Sub

This seems to work fine, I tested by putting the app in the background while I was watching TV and I would check periodically by bring the app back to the foreground and checking the graph I placed in my main Screen which shows both Voltage and Current signals, I also tested by browsing other apps while my app was in the background and I noticed the app kept running, I was able to save a csv file with a ton of data and here's what my chart looks like


Thanks to everyone for your suggestions, I will continue to test this, one thing I noticed is that the notification doesn't show up when the service starts, unless I switch
this line here
Service.AutomaticForegroundMode = Service.AUTOMATIC_FOREGROUND_WHEN_NEEDED
to this
Service.AutomaticForegroundMode = Service.AUTOMATIC_FOREGROUND_ALWAYS
But if I do this then the app will not run as long and it will crash with this error
java.lang.RuntimeException: android.app.ForegroundServiceStartNotAllowedException: startForegroundService() not allowed due to mAllowStartForeground false: service com.genesis.fnb58/.logdata

So for now, I am happy with the results, again thanks everyone for your advise and suggestions.

Regards,
Walter
 
Upvote 0

drgottjr

Expert
Licensed User
Longtime User
I decided to go with a Service since upon reading on Receivers, I noted that they are short lived or only used to run for a short period of time.

i don't mean to seem like i'm pushing receivers onto you, but you misunderstand what receivers do. they don't necessarily serve any purpose in your case, but they could. either in this case or later. a receiver can put into play things that are much longer running. imagine you have to make a long telephone call at some time in the middle of the night. you are normally asleep at that time, so you set your alarm to wake you up so that you can make that call. the alarm was only "short lived or only used to run for a short period of time". that's a receiver. with android, a receiver could start a longer running service. the receiver itself might only be active for the short time it takes to fire up the service. it's a tool to receive broadcasts, often from the system, designed to trigger some action, which action is not necessarily carried out by the receiver itself.
 
Last edited:
Upvote 0
Cookies are required to use this site. You must accept them to continue using the site. Learn more…