/, General tips, Using APIs/Monitoring location updates on Android

Monitoring location updates on Android

Monitoring location updates on Android in a consistent fashion (e.g in a service, and while the screen is locked) can be quite a challenge. This article tackles the problem head-on

The code discussed in this article was designed using Delphi 10.2 Tokyo Release 2, however it should at least work on Berlin, and possibly Seattle. For those who want to make a fast start, go to the Kastri Free project and download it. The demo project for this article is in the Demos\AndroidLocation folder.

Developing an app with location updates is, on the face of it, fairly straightforward. You create a new Firemonkey app, put a TLocationSensor on the form, and away you go. However, if your intention is to track the device while the app is not even running, you need to use a service. Even if you’re tracking just using an app, when the screen is locked, the situation can change. This discussion focuses on the former problem, i.e. tracking the device when the app is not even running.

These are the main points that I’ll cover:

  • Starting the service when the device is turned on, or “restarted”
  • Monitoring the device for when the screen is locked/unlocked, and when entering/exiting “doze” mode
  • Moving the service into the “foreground”, so that it still has network access even when the screen is locked
  • Catering for “doze” mode
  • Other techniques used in the demo
  • Communicating with a server

Starting the service on boot or restart

If you have a service that tracks the device location, you might want it to start when the device is turned on, or when it is restarted (which can be two different things now), so that tracking starts immediately. You may have in the past seen articles that cover being able to start an application at boot, however what if the user does not necessarily want to see the app start when the device starts?

The answer is in some Java code I have devised that acts as a “multipurpose” broadcast receiver. The code is part of the Kastri Free project, in DWMultibroadcastReceiver.java. This code will respond when the device boots, and take action depending on which intent actions are being filtered, including starting the service, and monitoring alarms.

In order to be able to start the service at boot or restart, and to monitor for the “doze” alarm, there are a couple of entries made in the manifest, as per this image:

Monitoring for screen lock and “doze” mode

When the screen is locked, after a period of time on later versions of Android, the system restricts network access to processes that are running in the foreground. To make matters worse, on later versions of Android, if the device is locked for an extended amount of time (somewhere around an hour on my Nexus 5X running Android 8.1) it can enter what is called “doze” mode, where it may not receive location updates.

To monitor for when the screen is locked or unlocked, or enters or exits “doze” mode, the service needs to register a receiver to listen for broadcasts for those specific intent actions. In the demo code, this is handled by the TServiceReceiver class, which forwards the intent on to the service.

“Moving” the service into the foreground

To alleviate the network access problem when the screen is locked, the service needs to be “moved” into the foreground. When the service detects the screen is locked, it calls StartForeground, which puts it into the foreground state. Foreground services require that a notification is supplied, which means that a notification icon would normally appear (if the screen was unlocked) in the status bar. When the screen is unlocked, the service calls StopForeground, which puts it out of foreground state. Since the service is in the foreground only when the screen is locked, the user will notice the notification only when the lock screen is viewed:

Catering for “doze” mode

When in this state, the only way to keep posting regular updates (although the device will be stationary anyway) is by use of an alarm, set with the AlarmManager, and in a special “allow when idle” mode. When using an alarm in this mode, the documentation says that it should be set at no more than once per 9 minutes.

As above, the service monitors for changes in lock mode, and when entering lock mode, sets an alarm. When the alarm goes off, the multipurpose receiver (described in the first section) “starts” the service (although in this case it has already started), and in the AndroidServiceStartCommand event updates the location using getLastKnownLocation, and resets the alarm.

Other techniques used in the demo

If you study the code, you may notice that it does not use TLocationSensor. Part of the reason for this is that it needs an instance of the Android LocationManager anyway (JLocationManager in Delphi) in order to call getLastKnownLocation, and I wanted to make sure that I had absolute control over how the listeners were set up etc. It’s quite possible that the same result could be achieved by using TLocationSensor.

Also, you may notice the use of a timer (not an FMX TTimer, though), which is set to a 4 minute interval. This is a “hangover” from experimentation of location updates when the screen is locked, and when the device enters doze mode. I have left it in, in case it might be needed in the future.

Communicating with a server

The whole point of the demo is so that when a location update is available, it can be sent to a server of some kind. In this case, it’s geared towards sending a JSON request to (presumably) a REST server. If you use this part of the demo, you could modify the cLocationUpdateURL to one of your own, and modify cLocationRequestJSON to suit your own requirements.

If you’re just interested in experimenting with the demo and don’t have your own server, you may contact me, and I can set you up to access mine.

As always, instead of explaining a lot of the code, I will field questions if you have any.

I’d like to thank Atek Sudianto, from Jakarta, Indonesia who set me down this path. I am in the planning stages of an app that requires all of what I’ve covered in this article, and his enquiries gave me the “prod” I needed.

By |2018-01-19T19:14:51+00:00January 19, 2018 6:52 pm|Code tips, General tips, Using APIs|28 Comments

About the Author:


  1. Bruno L January 30, 2018 at 9:33 am - Reply

    Thanks. This was a great read. Greetings from Brazil.

  2. alissonrodrigo February 23, 2018 at 11:14 pm - Reply

    Very good thank you! I have a doubt I have a project that used TMaps but I added the PUSH of Firebase according to this article (http://blog.delphiworlds.com/2017/05/add-firebase-cloud-messaging-mobile-apps- part-1 /) so that TMaps no longer responds because of Lib. I wonder if I can add this service to my application and TMaps would work again? Thanks again

    • Dave February 23, 2018 at 11:51 pm - Reply

      What do you mean by “no longer responds”? The libraries required by TMapView are included in the play-services-base.jar, which is added to the project, so it should still work.

  3. markius April 8, 2018 at 5:26 am - Reply

    Thank you very much for this article and for sharing the demo. It’s really great work and it’s very helpful due to lack of information regarding this topic.

  4. Eduardo Quintela April 16, 2018 at 1:24 pm - Reply

    I’m really thankful for the post. I’d like to know how to install the component to use AndroidLocation.

    • Dave April 16, 2018 at 1:32 pm - Reply

      There is no component related to this article to install. Are you having a specific problem with the code?

      • Eduardo Quintela April 16, 2018 at 11:50 pm - Reply

        Thank you very much for your attention.

        Sorry for the writing here because I’m a Brazilian using google translate.

        I was able to generate the DEMO AndroidLocation today, but the APP closes after a few seconds of started, can you help with some information?

        I am currently using the Delphi version 10.1 berlin upd 2.

  5. Arczi July 5, 2018 at 1:45 am - Reply
    • Dave July 5, 2018 at 6:37 am - Reply

      I developed the demo for this article on an Android 8 (API 26) device, taking into account the background limits (which is why the AlarmManager is used). Is there a specific problem you are having?

  6. Sergey September 7, 2018 at 8:38 pm - Reply

    Thanks for great example. I tried to use your code without the location listeners, becase all I need is to check updates on my server on the timer event. And the timer stops working after reboot of the phone (and after shut down -> start). It looks like Android doesn’t pause the application just because it uses GPS.

    • Dave September 7, 2018 at 9:29 pm - Reply

      The timer *should* work after a reboot, so I don’t know why it would not. I need to update the demo anyway, so I will check that soon

      • Sergey September 9, 2018 at 4:43 pm - Reply

        I was wrong about restart – it does not matter. It pauses when I remove the USB cable (so it works on the battery), and the phone goes to the sleep mode. In this case during the sleep mode the timer event does not work. After I unlock the phone, it continues working without problems. As I said, this problem occurs only if I remove the GPS-related code from your source. But I think, for your readers, it may be interesting to have an always-working service, not dependent on GPS and external power supply. By the way, the OS version is 5.0.2. Thanks!

        • Dave September 10, 2018 at 7:38 am - Reply

          I’ve now updated the source for the demo. The Pause/Resume functions now turn the location services on/off completely, and I’ve added support for API 26+

          • Sergey September 11, 2018 at 4:16 pm

            Thanks for update! But something strange happens with the Pause/Resume button. It updates the caption first time when I click on it (switches to “Resume Updates”), but the GPS still work. And after that it “does not react” to my clicks (the caption is always “Resume Updates”), although it’s enabled.

          • Dave September 11, 2018 at 4:58 pm

            I think I may know what the problem is. I’m working on another update to solve the issue.. stay tuned.

          • Dave September 13, 2018 at 8:34 am

            I think I have everything working as it should now. Please make sure you have all changed and new files in the demo project, ensure you have the updated dw-multireceiver.jar in the Lib folder, and ensure you do a Clean/Build. If you have further issues, please report it to here: https://github.com/DelphiWorlds/KastriFree/issues

          • Sergey September 14, 2018 at 5:51 am

            Thanks, the “Pause/Resume Updates” works without problems now. But this has nothing to do with the initial problem I described: when you pause location updates and switch your phone into sleep mode, the timer stops working (that is, Android pauses the service). Nothing has changed here 🙁

          • Dave September 14, 2018 at 8:12 am

            Is this for when the device is rebooted? If so, I’ve just checked in fixes (including dw-multireceiver.jar) so that the service actually starts properly on boot for API 26+ (for lower targets it should still work)

            For the general case, I tested the timer by making the interval 30 seconds (cLocationMonitoringInterval value of 30000) and watching the messages in Monitor – it works fine for me.

          • Sergey September 14, 2018 at 2:39 pm

            >Is this for when the device is rebooted?
            No. 1) Disconnect the phone from the power source (let it work on the battery). 2) Turn off location updates (click on your button) and close the application. 3) Put the phone into sleep mode. 4) Wait for 2-3 minutes. 5) Unlock the phone. 6) Run application and watch the log: can you see the TimerHandler for the past 2-3-minutes? I can’t.

          • davidnottage September 18, 2018 at 5:20 pm

            Fixed in the latest update, through the use of a wake lock. Make sure you update the changed files, including dw-multireceiver.jar

          • Sergey September 19, 2018 at 6:37 pm

            Very strange… I’ve updated all KastriFree folder, but the Timer still doesn’t work in sleep mode when updates are disabled. I can see your changes – the application is not showing on the lock screen anymore that is great. The WakeLock is set in User Permissions. But the problem with the timer remains…
            It looks like an impossible task for the today’s Delphi platform – to get periodical updates from the server 🙁

          • Sergey September 19, 2018 at 8:39 pm

            I’ve added my messages to code to see what happens. The EnableWakeLock(true) wasn’t running because CheckBuildAndTarget(26) returns FALSE. Alhough I’m using the Android SDK 26.0.2:

  7. Jinhu September 10, 2018 at 10:51 pm - Reply

    how to turn of mock location or developer option?

    • Dave September 11, 2018 at 7:33 am - Reply

      Turn off, or turn on?

      If you mean “on”, on Android, use something like Fake GPS. It’s not possible on a real iOS device, however you can do it in Simulator using the Debug|Location option from the Simulator menu. For a real iOS device, you’d need to do it in code (i.e. simulate location updates yourself, not even using TLocationSensor)

  8. Gordon November 21, 2018 at 2:57 pm - Reply

    Hi, Thank you for all of your hard work. Ive been trying to compile your demo, and I am getting lots of path errors.. Im a little lost.. All Im trying to do is create an app for our delivery trucks.. It will be a foreground service, and will Always be running tracking the location of the driver. Every 3-5 minutes, it will send the long lat to my server. Have no trouble doing this in a normal app. I will use a mobile device manager to lock the phone down, so the driver can not remove the app. Is there some really straight forward demo app that I can start with, using simply Location and will run at startup? Im really lost with all of the options.

    • Dave November 22, 2018 at 7:40 am - Reply

      Please be aware that the demo relies on the Kastri Free library, so just downloading the project and files from the demo is not going to work. If you have downloaded the entire library, the demo project should compile from where it is located.

      The whole premise of the article is to ensure that location updates happen whether or not the application is running, or is in the background. If you know your app will never go into the background (which will happen if the screen becomes locked), and not be shut down, you may be able to get away with just using TLocationSensor as per the demo supplied with Delphi.

      Having said that, for apps that need location monitoring continuously (i.e. on startup, and whether or not the app is running or in the background), the demo is about as straightforward as it can be. If you do have the Kastri Free library and are compiling the demo from within it, please let me know what errors you are seeing.

  9. Gordon November 22, 2018 at 1:30 pm - Reply

    Thank you for the reply. I messed with it at length today.. I have all of the lib paths right in delphi, Bin, core dirs etc.. Your paths are fine. The problem I have is trying to figure out why when I try to compile your code (Vs other android code) it gives me this weird path error, saying it is trying to look for the file javac.exe .. in weird dirs. I cant find where those weird paths are stored and then appended to my Environment path..

    Build started 11/21/2018 8:57:57 PM.
    Project “C:\_Delphi Tools\KastriFree-master\Demos\AndroidLocation\Service\LocationService.dproj” (Make target(s)):
    Target BuildVersionResource:
    c:\program files (x86)\embarcadero\studio\19.0\bin\cgrc.exe -c65001 “LocationService.vrc” -foLocationService.res
    CodeGear Resource Compiler/Binder
    Version 1.2.2 Copyright (c) 2008-2012 Embarcadero Technologies Inc.

    Microsoft (R) Windows (R) Resource Compiler Version 6.0.5724.0

    Copyright (C) Microsoft Corporation. All rights reserved.

    Deleting file “LocationService.vrc”.
    Target BuildAndroidServiceJarFile:
    “\bin\javac” -d “C:\_Delphi Tools\KastriFree-master\Demos\AndroidLocation\Service\JavaClasses\LocationService” -Xlint:deprecation -classpath “C:\Users\Public\Documents\Embarcadero\Studio\19.0\CatalogRepository\AndroidSDK-2433_19.0.31059.3231\platforms\android-22\android.jar”;”c:\program files (x86)\embarcadero\studio\19.0\lib\Android\Debug\fmx.jar” -bootclasspath “C:\Users\Public\Documents\Embarcadero\Studio\19.0\CatalogRepository\AndroidSDK-2433_19.0.31059.3231\platforms\android-22\android.jar” -encoding UTF-8 -target 1.6 -g -source 1.6 .\Android\Debug\LocationService.java .\Android\Debug\LocationServiceProxyInterface.java
    The system cannot find the path specified.
    c:\program files (x86)\embarcadero\studio\19.0\bin\CodeGear.Common.Targets(811,5): error MSB3073: The command “”\bin\javac” -d “C:\_Delphi Tools\KastriFree-master\Demos\AndroidLocation\Service\JavaClasses\LocationService” -Xlint:deprecation -classpath “C:\Users\Public\Documents\Embarcadero\Studio\19.0\CatalogRepository\AndroidSDK-2433_19.0.31059.3231\platforms\android-22\android.jar”;”c:\program files (x86)\embarcadero\studio\19.0\lib\Android\Debug\fmx.jar” -bootclasspath “C:\Users\Public\Documents\Embarcadero\Studio\19.0\CatalogRepository\AndroidSDK-2433_19.0.31059.3231\platforms\android-22\android.jar” -encoding UTF-8 -target 1.6 -g -source 1.6 .\Android\Debug\LocationService.java .\Android\Debug\LocationServiceProxyInterface.java” exited with code 3.
    Done building target “BuildAndroidServiceJarFile” in project “LocationService.dproj” — FAILED.
    Done building project “LocationService.dproj” — FAILED.
    Build FAILED.
    c:\program files (x86)\embarcadero\studio\19.0\bin\CodeGear.Common.Targets(811,5): error MSB3073: The command “”\bin\javac” -d “C:\_Delphi Tools\KastriFree-master\Demos\AndroidLocation\Service\JavaClasses\LocationService” -Xlint:deprecation -classpath “C:\Users\Public\Documents\Embarcadero\Studio\19.0\CatalogRepository\AndroidSDK-2433_19.0.31059.3231\platforms\android-22\android.jar”;”c:\program files (x86)\embarcadero\studio\19.0\lib\Android\Debug\fmx.jar” -bootclasspath “C:\Users\Public\Documents\Embarcadero\Studio\19.0\CatalogRepository\AndroidSDK-2433_19.0.31059.3231\platforms\android-22\android.jar” -encoding UTF-8 -target 1.6 -g -source 1.6 .\Android\Debug\LocationService.java .\Android\Debug\LocationServiceProxyInterface.java” exited with code 3.
    0 Warning(s)
    1 Error(s)
    Time Elapsed 00:00:00.17

Leave a Reply

Show Buttons
Hide Buttons