Android Question Stopwatch and Services

brodmuehler

Member
Licensed User
Longtime User
Hi Everybody

I programmed a little and very simple stopwatch in order to learn how Services work. But this stopwatch is not working since it always falls behind as soon as my phone switches off. That means that I am doing something wrong.

Could someone perhaps have a look at my code and give me a hint what is wrong. Iam runningout of ideas and I cannot find a solution in the tutorials and examples.

I attached my app as a zip-file.

Thanks a lot and kind regards
René
 

Attachments

  • TestService.zip
    319.2 KB · Views: 232

walterf25

Expert
Licensed User
Longtime User
Hi Everybody

I programmed a little and very simple stopwatch in order to learn how Services work. But this stopwatch is not working since it always falls behind as soon as my phone switches off. That means that I am doing something wrong.

Could someone perhaps have a look at my code and give me a hint what is wrong. Iam runningout of ideas and I cannot find a solution in the tutorials and examples.

I attached my app as a zip-file.

Thanks a lot and kind regards
René
Seems to be working fine, i don't see the issue you're describing, the service restarts every second.

Cheers,
Walter
 
Upvote 0

brodmuehler

Member
Licensed User
Longtime User
Hi walterf25

I put my tablet with a stopwatch app beside my phone, started both at the same time and let them run. After a while my phone switches off, as it should do to save energy. When my tablet showed 2 minutes I switched my phone back on and my app only showed 105, where it should have shown 120. The longer I wait the bigger the difference.

Regards
René
 
Upvote 0

walterf25

Expert
Licensed User
Longtime User
Hi walterf25

I put my tablet with a stopwatch app beside my phone, started both at the same time and let them run. After a while my phone switches off, as it should do to save energy. When my tablet showed 2 minutes I switched my phone back on and my app only showed 105, where it should have shown 120. The longer I wait the bigger the difference.

Regards
René
I don't see any count on the notification, did you forget to add that part to your code?, The reason i say it works is because i can see the service starting every 1 second even after the phone's screen turns off.

Walter
 
Upvote 0

walterf25

Expert
Licensed User
Longtime User
I don't see any count on the notification, did you forget to add that part to your code?, The reason i say it works is because i can see the service starting every 1 second even after the phone's screen turns off.

Walter
I think i know what you mean, you have the counter on the main activity, in this case the behavior you are seeing is normal, because you are trying to access a view which is on the main activity from a service. The main activity is not accessible from a service when the screen is off, if you were to display the counter in the notification you will see that counter keeps updating regardless of whether the screen is on or off.

Cheers,
Walter
 
Upvote 0

brodmuehler

Member
Licensed User
Longtime User
I think i know what you mean, you have the counter on the main activity, in this case the behavior you are seeing is normal, because you are trying to access a view which is on the main activity from a service. The main activity is not accessible from a service when the screen is off, if you were to display the counter in the notification you will see that counter keeps updating regardless of whether the screen is on or off.

Cheers,
Walter

Hi Walter

thanks for your responses. I have to say that I struggle a bit to get my head around the issue. I thought, that the counter itself is on the Service and hence keeps running. Theonly think that is on the main activity is the label that dispalys the value, it gets from the Service with the counter. I also was of the impression that you actually cannot declare a view in a Service and that I must hand it over to the main activity for display. I must have missed something.

Regards
René
 
Upvote 0

walterf25

Expert
Licensed User
Longtime User
T
Hi Walter

thanks for your responses. I have to say that I struggle a bit to get my head around the issue. I thought, that the counter itself is on the Service and hence keeps running. Theonly think that is on the main activity is the label that dispalys the value, it gets from the Service with the counter. I also was of the impression that you actually cannot declare a view in a Service and that I must hand it over to the main activity for display. I must have missed something.

Regards
René
The counter will keep running as long as the service is kept alive, but the label in the main activity will not keep updating if the screen is turned off, this is because the main activity gets paused when the screen turns off, once you turn the screen back on the main activity resumes and you will notice that the value on the label will jump from where it left off when the screen turned off to where the counter is currently at when the screen turns on again.

Hope this explains it a little better.
Walter
 
Upvote 0

brodmuehler

Member
Licensed User
Longtime User
Hi Walter
that is exactly what I thought would happen and I actually can see the time jumping when I switch the phone back on and the activity resumes. But there is something that is not working properly. I did the following experiment: I started my stopwatch and the stopwatch on an iPad at the same time. I let my stopwatch running for 10 minutes and avoided that my phone switched off. I stopped both stopwatches and took picture B1. As you can see the stopped time is pretty much the same on both devices. Then I started both stopwatches again but this time I allowed my phone to switch off. I switched my phone back on after 10 minutes and took a second picture B2 from both stopwatches. On B2 you can easily see that the time difference is huge. As far as I can conclude there must be something wrong with my Service module.
Do you have any idea?
Kind regards
René
 

Attachments

  • B1B2.zip
    394.3 KB · Views: 206
Upvote 0

Straker

Active Member
Licensed User
Longtime User
I'm not very sure, but I think that you cannot have a service restarted each second. When your phone goes to sleep it slows everything down in order to minimize power consumption. Normally service are used for 'slow' tasks which works in background (for example download a huge file, check for something avery hour...) not to perform actions every single second.

I suppose that if your stopwach will count every 5 minutes and not every second, it will work just fine.
 
Upvote 0

brodmuehler

Member
Licensed User
Longtime User
I'm not very sure, but I think that you cannot have a service restarted each second. When your phone goes to sleep it slows everything down in order to minimize power consumption. Normally service are used for 'slow' tasks which works in background (for example download a huge file, check for something avery hour...) not to perform actions every single second.

I suppose that if your stopwach will count every 5 minutes and not every second, it will work just fine.

Hi
That could well be the case. Going to a 5 minute interval does not support what I want to achieve. There must be a way to make this work. There countless stopwatch apps out there that work fine also when the device go to sleep. I guess I just need to find the right way to do it. Thanks for looking into this and please let me know when you have any idea. Highly appreciateed.
Regards
Rene
 
Upvote 0

walterf25

Expert
Licensed User
Longtime User
Hi
That could well be the case. Going to a 5 minute interval does not support what I want to achieve. There must be a way to make this work. There countless stopwatch apps out there that work fine also when the device go to sleep. I guess I just need to find the right way to do it. Thanks for looking into this and please let me know when you have any idea. Highly appreciateed.
Regards
Rene
Actually there is no need to re-start your service every second, you can simply keep the timer running inside the service at a 1 second interval, i forgot to mention that to you yesterday, there is no reason you need to re-start your service every second.

Cheers,
Walter
 
Upvote 0

brodmuehler

Member
Licensed User
Longtime User
Actually there is no need to re-start your service every second, you can simply keep the timer running inside the service at a 1 second interval, i forgot to mention that to you yesterday, there is no reason you need to re-start your service every second.

Cheers,
Walter

Hi Walter
thanks again. That was an excellent idea and I think it improved the situation. But my stopwatch is still lagging behind and the delta still gets bigger over time. The timer is in milliseconds, isn't it?
Thanks and regards
René
 
Upvote 0

walterf25

Expert
Licensed User
Longtime User
Hi Walter
thanks again. That was an excellent idea and I think it improved the situation. But my stopwatch is still lagging behind and the delta still gets bigger over time. The timer is in milliseconds, isn't it?
Thanks and regards
René
Maybe i'm confused, but i don't really understand what the problem is.
Can you explain again how is it that you are getting that huge difference between both stop watches.
When you turn off the screen the counter will continue running since it is running inside a service, however the label in your main activity will stop updating since the main activity is paused.
If you add the counter to the notification bar, you will notice that it maintains accurate.

Regards,
Walter
 
Upvote 0

brodmuehler

Member
Licensed User
Longtime User
Maybe i'm confused, but i don't really understand what the problem is.
Can you explain again how is it that you are getting that huge difference between both stop watches.
When you turn off the screen the counter will continue running since it is running inside a service, however the label in your main activity will stop updating since the main activity is paused.
If you add the counter to the notification bar, you will notice that it maintains accurate.

Regards,
Walter

Sorry Walter
I really do not want to confuse you. Everything you are saying is correct. Turning off the screen stops the label to be updated and when I turn the screen back on the number in the label jumbs up. The counter in the service keeps running in the background but compared to the reference stopwatch, my counter is too slow.

When you say I should "add the counter to the notification bar", what does that really mean. I attached my code again with the changes I made so far. I really do want to waste your time, but perhaps you have a moment to have a look at my code. You might be able to spot the problem straight away. What happens if you run my stopwatch against one of yours, assuming you have a second device with a stopwatch. Perhaps there is something wrong with my phone.

Thanks a lot for your patience.
Kind regards
René
 

Attachments

  • TestService.zip
    320.1 KB · Views: 183
Upvote 0

RandomCoder

Well-Known Member
Licensed User
Longtime User
I think that your problem is that you need to set a PartialLock to prevent the CPU from going to sleep. To do this select "Phone: v2.26" from the libraries list and then add a PartialLock to your code, see pws entries below...
B4X:
Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'These variables can be accessed from all modules.
 
    Dim Timer1 As Timer
    Dim pws As PhoneWakeState
End Sub

Sub btnStart_Click
 
    Timer1.Enabled=True
      StartService(TestService)
    ToastMessageShow("Service Started!", False)
    pws.PartialLock
End Sub

Sub btnStop_Click

    StopService(TestService)
    Timer1.Enabled=False
    pws.ReleasePartialLock
 
End Sub
 
Upvote 0

brodmuehler

Member
Licensed User
Longtime User
Hi
That really made a difference. It is still lagging a little bit compared to my reference stopwatches in my Android and Apple tablet but close enough that I can use it for what I want to do. However, should someone wants to create a real accurate stopwatch my code would not work. Something is still wrong.
Thanks a lot for the great help. It is recognized and highly appreciated. Regards Rene
 
Upvote 0

RandomCoder

Well-Known Member
Licensed User
Longtime User
The problem with accuracy is a different one. I believe that the timer internal cannot be relied on for accuracy due to the nature in which tick events are queued in the Android system. You will probably need to sense check and correct the accumulated time using the system clock maybe every 5 minutes, just an idea?
 
Upvote 0

brodmuehler

Member
Licensed User
Longtime User
The problem with accuracy is a different one. I believe that the timer internal cannot be relied on for accuracy due to the nature in which tick events are queued in the Android system. You will probably need to sense check and correct the accumulated time using the system clock maybe every 5 minutes, just an idea?

Interesting point. Would be good to understand how the other apps are coded. Is there an alternative to the timer that goes back to a more accurate signal. Regards René
 
Upvote 0

fixit30

Active Member
Licensed User
Longtime User
Another approach for a truly accurate stopwatch would be to store the datetime into a variable when the stopwatch is started, and then with each tick calculate the elapsed time between datetime.now and the start time.
 
Upvote 0

RandomCoder

Well-Known Member
Licensed User
Longtime User
I didn't expect this to cause me as much trouble as it has! I ran into an unexpected problem whereby storing DateTime.Now when the service is created then subtracting it from DateTime.Now with each tick of the timer resulted in an additional hour appearing for some reason???
I don't understand this but I also found that DateTime.Time(0) results in a hour being returned, is this a bug?
Anyhow, luckily the problem has been experienced before Time format & milliseconds and @Erel has come to the rescue again.

Here is your example using a slightly modified version that excludes the milliseconds...

Main Activity...
B4X:
#Region  Project Attributes
    #ApplicationLabel: ServiceExample
    #VersionCode: 1
    #VersionName:
    'SupportedOrientations possible values: unspecified, landscape or portrait.
    #SupportedOrientations: unspecified
    #CanInstallToExternalStorage: False
#End Region

#Region  Activity Attributes
    #FullScreen: False
    #IncludeTitle: True
#End Region

Sub Process_Globals
    Dim Timer1 As Timer
    Dim pws As PhoneWakeState
End Sub

Sub Globals
    Dim btnStart As Button
    Dim lblCount As Label
End Sub

Sub Activity_Create(FirstTime As Boolean)
    Activity.LoadLayout("MyLayout")
    Timer1.Initialize("Timer1",250)
End Sub

Sub Activity_Resume
End Sub

Sub Activity_Pause (UserClosed As Boolean)
End Sub

Sub btnStart_Click
    Timer1.Enabled=True
      StartService(TestService)
    ToastMessageShow("Service Started!", False)
    pws.PartialLock
End Sub

Sub Timer1_Tick
      lblCount.Text=FN(TestService.tp.Hours) & ":" & FN(TestService.tp.Minutes) & ":" & FN(TestService.tp.Seconds)
End Sub

Sub btnStop_Click
    StopService(TestService)
    Timer1.Enabled=False
    pws.ReleasePartialLock
End Sub

Sub FN(n As Int) As String
   Return NumberFormat(n, 2, 0)
End Sub

Service Module...
B4X:
#Region  Service Attributes
    #StartAtBoot: False
    #StartCommandReturnValue: android.app.Service.START_STICKY
#End Region

Sub Process_Globals
    Dim TimerService As Timer
    Dim Noti1 As Notification
    Dim ts As Long    ' TimeStarted
    Dim te As Long    ' TimeElapsed
    Dim tp As Period' TimePeriod
End Sub

Sub Service_Create
    ts = DateTime.Now
    TimerService.Initialize("TimerService",200)
      TimerService.Enabled=True
  
    Noti1.Initialize
    Noti1.Icon = "icon" 'use the application icon file for the notification
    Noti1.Vibrate = False
    Noti1.Sound = False  
    Noti1.Light=False
    Noti1.SetInfo("Service Example", "Counter", Main)
    Service.StartForeground(1, Noti1)
End Sub

Sub Service_Start (StartingIntent As Intent)
End Sub

Sub Service_Destroy
    Service.StopForeground(1)
    ToastMessageShow("Service Stopped!", False)
End Sub

Sub TimerService_Tick
    te = DateTime.Now
    tp = PeriodBetweenWithMilliseconds(ts, te)
End Sub


Sub PeriodBetweenWithMilliseconds(time1 As Long, time2 As Long) As Period
   Dim p As Period = DateUtils.PeriodBetween(time1, time2)
   Return p
End Sub
 
Upvote 0
Top