1

I'm trying to write a FireMonkey app, which must do some work in background (not very intensive task), even if the user of the device switches to another app or the display turns off. In this task I periodically send an HTTP request (with TIdHTTP) to a server to get a small piece of data and to test if the app is still running.

By default, Android is closing apps in background or when the user turns off the display. I tried two methods to keep my app running:

  1. Adding the app through the Androids UI to the list of "protected apps" (disabling power optimizations for the app / "keep running while screen is off").

  2. I created a local service (TAndroidService) with the sticky flag (START_STICKY) through the project wizard and moved the task code there.

Starting service from host application:

// LIntent and LServiceName are stored in TForm1
procedure TForm1.StartMyService();
begin
  LIntent := TJIntent.Create();
  LServiceName := 'com.embarcadero.services.MyService';
  LIntent.setClassName(TAndroidHelper.Context.getPackageName(), TAndroidHelper.StringToJString(LServiceName));
  TAndroidHelper.Activity.startService(LIntent);
end;

The OnStartCommand event in the service looks like this:

function TMyServiceDM.AndroidServiceStartCommand(const Sender: TObject;
  const Intent: JIntent; Flags, StartId: Integer): Integer;
begin
  Result := TJService.JavaClass.START_STICKY;

  LThread := TThread.CreateAnonymousThread(procedure
    begin

      while True do
      begin
        // ... Task performed here periodically and exits loop when needed ...

        // Sleep for 1 minute
        LThread.Sleep(60000);
      end;

      // Before thread stops, also stop the Java service
      JavaService.stopSelf(StartId);
    end);

  LThread.FreeOnTerminate := False;
  LThread.Start();
end;

The app keeps working in background and even if the display is turned off BUT only while the device is connected through a cable with a power source (e. g. over a USB cable with my machine). If the power cable is removed, the app seems to stop instantly when the screen turns off, even if the app stays in foreground.

Do I do something wrong? How can I keep my service doing its job in background (like messenger apps which do something similar)? I know, that Android 6.0 (API level 23) and upwards use a feature called "Doze" and Google want their devs to use FCM (Firebase Cloud Messaging). I read about it only recently and hoped that at least disabling the power optimizations for the app would fix this problem, but no.

StanE
  • 2,704
  • 29
  • 38
  • 1
    The demo associated with this article: https://www.delphiworlds.com/2018/01/monitoring-location-updates-on-android/ is similar to what you appear to want to achieve. It handles scenarios like start-at-boot, app moving to background and lock screen – Dave Nottage Sep 26 '18 at 00:12
  • @DaveNottage i forget you did this demo, i asked a similar question few day ago about oreo+ (https://stackoverflow.com/questions/52479776/oreo-how-to-catch-location-update-from-the-background) ... did you succeed to catch location update without a notification to the end user (ie: startforegroundservice) – zeus Sep 26 '18 at 05:49
  • @loki No, I haven't found a way.. I think Markus Penguin explains it pretty clearly in his answer. I plan to look into JobIntentService sometime: https://developer.android.com/reference/android/support/v4/app/JobIntentService – Dave Nottage Sep 26 '18 at 07:04

1 Answers1

0

For example With android pre-oreo you can do this by doing :

in AndroidManifest.xml

  <receiver android:name="com.myapp.StartServiceBroadcastReceiver">  
    <intent-filter>  
      <action android:name="android.intent.action.BOOT_COMPLETED" />  
    </intent-filter>  
  </receiver> 

in the StartServiceBroadcastReceiver i do :

  @Override
  public void onReceive(Context context, Intent intent) {

    try {

        /* start the location service */  
        Intent startServiceIntent = new Intent(context, LocationService.class);
        context.startService(startServiceIntent);

    } catch (Throwable e){ Log.e(TAG, "Exception", e); }  

  }

but this not work anymore on android oreo+ ! now startservice return

Not allowed to start service Intent

you need to call instead startForegroundService, but this have a huge drawback to show a notification icon to the end user :(

zeus
  • 12,173
  • 9
  • 63
  • 184